'Char' is not a member of 'String'" in VB.NET - vb.net

Scenario is described below:
In txtDiscountRate.Text it has a value which is "0.996010500406591".
In my coding I did this:
txtDiscountRate.Text = Math.Round(Val(txtDiscountRate.Text.Trim), 6) 'means considering round till 6th digit
It is giving the value 0.99601 because of 6th digit after decimal is 0. But I want to put a condition in the decimal value.
So if on 6th digit after decimal is ( 0 or 1 or 2 or 3 or 4 ) and 7th digit after decimal is available, then it will round until 7th position.
Or else it will round until 6th position.
I got a solution (shown in the code below) from this site for this problem. I tried to implement but the below code throws an error:
CInt(Str(1).Char(5)) is showing error this error -> " 'Char' is not a member of 'String'".
This is my whole code so far:
Dim str() As String = Split(CStr(Dec), ".")
If CInt(Str(1).**Char(5)**) < 5 Then 'It's Char number 5 since it's a zero-based index. So the first number = Index 0.
txtDiscountRate.Text = Math.Round(Val(txtDiscountRate.Text.Trim), 7)
Else
txtDiscountRate.Text = Math.Round(Val(txtDiscountRate.Text.Trim), 6)
End If
Correct solution is needed.

I think that following function will help you and it is your solution.
Function GetDecimalValue(ByVal value As String) As Decimal
Dim valueFromDot As String = value.Substring(IIf(value.IndexOf(".") < 0, 0, value.IndexOf(".") + 1))
If (valueFromDot.Length > 5) Then
If (CInt(valueFromDot(5).ToString()) < 5 And CInt(valueFromDot(6).ToString()) > 0) Then
GetDecimalValue = Math.Round(CDec(value), 7)
Else
GetDecimalValue = Math.Round(CDec(value), 6)
End If
Else
GetDecimalValue = CDec(value)
End If
End Function

Related

(Visual Basic) Sum of integers through 2 numbers

So im pretty close but I continue to get the wrong values. The user is suppose to enter a positive integer and its suppose to add all the integers in between. So if the user enters 5 it should equal 15, 10 would equal 55, etc. But I get 5 = 25, 10, 100.
Changed to decimal to see if that had anything instead of integer and still did nothing. I saw a few things to set decCount to = 1. Did that and the number was closer but still not there.
Dim decSum As Decimal = 0
Dim decNumber As Decimal = 0
Dim decCount As Decimal = 0
Dim strUserInput As String
strUserInput = InputBox("Enter a positive integer value.", "Input Needed", 0)
If Decimal.TryParse(strUserInput, decNumber) And (decNumber >= 0) Then
Do While decCount < decNumber
decSum = decSum + decNumber
decCount = decCount + 1
Loop
Else
MessageBox.Show("Enter a positive numeric value")
End If
MsgBox("The sum of the numbers 1 through " & decNumber & " is " & decSum)
You are trying to calculate a factorial of a given input, but in your loop you are adding the same number repeatedly (effectively, you are multiplying the number by itself instead of finding the factorial).
Change this line:
decSum = decSum + decNumber
to this:
decSum = decSum + decCount

128 bit hex keygen

So my professor gave me a challenge to build a decoder that could break his special formula. It was described to be 32 characters in length, alphanumeric numeric when entered but then "it has a system... the first 106 bits must be 50% 1's and the rest 0's, the remaining 22 bits are basically a hash of the previous bits so that the key can be checked..." were his exact words. Sounds to me like a 128 bit encryption with a twist. I found the below but I need VB2010 or VS2010, this says php.
<?php
function string_random($characters, $length)
{
$string = '';
for ($max = mb_strlen($characters) - 1, $i = 0; $i < $length; ++ $i)
{
$string .= mb_substr($characters, mt_rand(0, $max), 1);
}
return $string;
}
// 128 bits is 16 bytes; 2 hex digits to represent each byte
$random_128_bit_hex = string_random('0123456789abcdef', 32);
// $random_128_bit_hex might be: '4374e7bb02ae5d5bc6d0d85af78aa2ce'
Would that work? Or does it need converting? Please help. Oh and thank you :)
I wasn't promised extra credit but either way I would like to surprise him.
So the first 106 bit are 26 character and the first half of the 27.
You have first of all encode somehow the number of 0 and 1, while building the string you need to keep an eye to the number. An idea would be to build a map like this:
0 = 0000 = -4
1 = 0001 = -2
2 = 0010 = -2
3 = 0011 = 0
4 = -2
5 = 0
6 = 0
7 = +2
8 = -2
9 = 0
a = 0
b = +2
c = 0
d = +2
e = +2
f = +4
then everytime you extract a new random number you check the number associated to it and add it to a variable
balanceOfOneAndZero
your objective is have balanceOfOneAndZero = 0 when you hit your 27th character.
to do that you need a control function, that takes current balanceOfOneAndZero, the proposed character proposedChar, and current string lenght currLenght.
Would be better to split the problem into two part. First is reaching the 26th character of the sequence with balanceOfOneAndZero between -2 and 2. Any other value is not acceptable, because your 27th character can have maximum two 1 or two 0 to completely balance the first 106 characters.
so your function should do something like (I'll write in sort of pseudo code since I don't have an IDE right now)
function checkNextLetter(Dim balanceOfOneAndZero As Integer, Dim proposedChar As Char,
Dim currentLenght as Integer) As Boolean
If( ((26 - currentLenght - 1) * 4 + 2) < MOD(Map.ValueOf(proposedChar) + balanceOfOneAndZero) ) Then
Return true
Else
Return false
ENd If
End function
This function basically check if accepting the new character will still make possible to Balance the number of 0 and 1 before the 26th character.
So your main function should have a loop every time it propose a new character, something like
proposedChar = new RandomChar
While (Not checkNextLetter(balanceOfOneAndZero, proposedChar, len(currentString))
proposedChar = new RandomChar
End While
currentString = currentString & proposedChar
this only until you hit the 26th character.
Than you have to check balanceOfOneAndZero, if its 2 you add a character that begin with 00, if it's 0 you can either have 10 or 01, if it's -2 you have to add a character that begin with 11.
After this I can't help you about the rest 22 character, since there are not enough information. You could brute force the rest
EDIT:
so to brute force the rest (il start from when you reach the 26th character):
Dim stringa1, stringa2, stringa3, stringa4 As String
If balanceOfOneAndZero = 2 Then
stringa1 = currentString & '0'
stringa2 = currentString & '1'
stringa3 = currentString & '2'
stringa4 = currentString & '3'
ELse If balanceOfOneAndZero = 0 Then
stringa1 = currentString & '4'
stringa2 = currentString & '5'
stringa3 = currentString & '6'
stringa4 = currentString & '7'
Else
stringa1 = currentString & 'c'
stringa2 = currentString & 'd'
stringa3 = currentString & 'e'
stringa4 = currentString & 'f'
End if
Function GenerateAllCombination(ByVal iLenght As Integer)
Dim arrayLista As New List(Of String)()
Dim arraySubLista As New List(Of String)()
If (iLenght > 1) Then
arraySubLista = GenerateAllCombination(iLenght -1)
for each objString As String in arraySubLista
for each ele As String in arrayValori
arrayLista.add(objString & ele)
loop
loop
Else
for each ele As String in arrayValori
arrayLista.add(ele)
loop
End If
End Function
Now if you use generateAllCombination you will have a List of string with ALL the combination of 5 character.
Now you just create 4 list by concatenating those combination with your string1 to string4 (string1 & combination) etc..
put all those result on a List of string, and you have 100% that at least ONE of the string will break your teacher code
I forgot, arrayValori must be a List with all values from "0" to "f"

how to check what the first character of a string is in vb

I have the following code, which reads the date and time from some DateTimePickers in VB.
I need to be able to determine if the first value is a 0 or a 1, (eg 09:12... or 12:13...) and if it starts with a 0 to remove that character from the string.
this is what i have so far, but it takes the first character regardless.
DateFrom = Form1.DateTimePickerFrom.Value.ToString
DateTo = Form1.DateTimePickerTo.Value.ToString
VarTimeFrom = Form1.HourTimePickerFrom.Value.ToString
VarTimeTo = Form1.HourTimePickerTo.Value.ToString
Dim DateFromManipulated = Left(DateFrom, 10)
Dim DateToManipulated = Left(DateTo, 10)
Dim TimeFromManipulated = Right(VarTimeFrom, 9)
Dim TimeToManipulated = Right(VarTimeTo, 9)
If Left(DateFromManipulated, 1) = 0 Then
TimeFromMan = TimeFromManipulated.Remove(0, 1)
Else
TimeFromMan = TimeFromManipulated
End If
If Left(TimeFromManipulated, 1) = 0 Then
TimeToMan = TimeToManipulated.Remove(0, 1)
Else
TimeToMan = TimeToManipulated
End If
Console.WriteLine(DateFromManipulated)
Console.WriteLine(TimeToMan)
Console.WriteLine(TimeFromManipulated)
Console.WriteLine(TimeFromMan)
Console.WriteLine(DateToManipulated)
Console.WriteLine(TimeToManipulated)
I get the following:
09/11/2012
1:36:00
06:36:00
6:36:00
08/01/2013
11:36:00
Thanks in advance!
Mike
A string in VB.NET won't compare as equal to an integer. You could just reference character zero, though:
If DateFromManipulated(0) = "0"c Then DateFromManipulated = DateFromManipulated.Substring(1)
... however, you should be just formatting your date the way you want it to begin with:
Dim dateFrom As String = DateTimePickerFrom.Value.ToString("M/dd/yyyy H:mm:ss")
... for example. (M doesn't have a leading zero, as opposed to MM; same with H.) You can find all the format strings here: http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx

Verify Gamefield VB.NET

So I'm developing a minesweeper game and im assigning the mines, but I've got to check where are the mines now, in order to generate the numbers. The problem is that when I'm verifying the columns and lines I need the program not to get out of the game field.
Here's how my code looks like now:
Public Sub avisinhos(ByVal line, ByVal column)
If mat(line, column) = 0 Then
mat(line, column) = -1
numbandeiras = numbandeiras + 1
End If
For auxlinha = -1 To 1
For auxcolumn = -1 To 1
Next
Next
End Sub
How do I create a IF function to verify that I don't get out of the game field?
Best regards, joao.
pseudo code
int linestart = -1;
int lineend = 1;
int colstart = -1;
int colend = 1;
Assuming a 10 x 10 grid (zero based)
if line < 2 linestart = 0
if line > 8 lineend = 0
if column < 2 colstart = 0
if column > 8 colend = 0
For auxlinha = linestart To lineend
For auxcolumn = colstart To colend
// check
Next
Next
Personally though I wouldn't bother with the loops, they add very little to nothing
HasMineAbove = (line > 1) and (gamefield[line -1,column] = MinePresentValue
would be my approach, do it all in one.
Not to mention the huge potential confusion when auxlinha and auxcolumn are both zero...
I'm not sure exactly what your code is saying. It's a bit cryptic since you're using abbreviations and all lowercase names. You might want to try camelCasing and spelling out the words more completely, intellisense is your friend. =)
But coding style aside, if you are trying to loop through a limited range of values, you can keep your values bounded by using the modulus operator (%). For example, if you need to keep you values between 0-7 and you end up with a value of 12, just take the modulus of 8 to loop back to within range with a value of 4:
12 % 8 = 4
9 % 8 = 1
15 % 8 = 7
24 % 8 = 0
I realize this doesn't answer your specific question, but it's a handy technique might find useful.

How can I convert a decimal to a fraction?

How do I convert a indefinite decimal (i.e. .333333333...) to a string fraction representation (i.e. "1/3"). I am using VBA and the following is the code I used (i get an overflow error at the line "b = a Mod b":
Function GetFraction(ByVal Num As Double) As String
If Num = 0# Then
GetFraction = "None"
Else
Dim WholeNumber As Integer
Dim DecimalNumber As Double
Dim Numerator As Double
Dim Denomenator As Double
Dim a, b, t As Double
WholeNumber = Fix(Num)
DecimalNumber = Num - Fix(Num)
Numerator = DecimalNumber * 10 ^ (Len(CStr(DecimalNumber)) - 2)
Denomenator = 10 ^ (Len(CStr(DecimalNumber)) - 2)
If Numerator = 0 Then
GetFraction = WholeNumber
Else
a = Numerator
b = Denomenator
t = 0
While b <> 0
t = b
b = a Mod b
a = t
Wend
If WholeNumber = 0 Then
GetFraction = CStr(Numerator / a) & "/" & CStr(Denomenator / a)
Else
GetFraction = CStr(WholeNumber) & " " & CStr(Numerator / a) & "/" & CStr(Denomenator / a)
End If
End If
End If
End Function
As .333333333 is not 1/3 you will never get 1/3 but instead 333333333/1000000000 if you do not add some clever "un-rounding" logic.
Here is a solution for handling numbers with periodic decimal representation I remember from school.
A number 0.abcdabcd... equals abcd/9999. So 0.23572357... equals 2357/9999 exactly. Just take that many 9s as your pattern is long. 0.11111... equals 1/9, 0.121212... equals 12/99, and so on. So try just searching a pattern and setting the denominator to the corresponding number. Of course you have to stop after some digits because you will never know if the pattern is repeated for ever or just many times. And you will hit the rounding error in the last digit, so you still need some clever logic.
This only works in Excel-VBA but since you had it tagged "VBA" I will suggest it. Excel has a custom "fraction" format that you can access via "Format Cells" (or ctrl-1 if you prefer). This particular number format is Excel-Specific and so does not work with the VBA.Format function. It does however work with the Excel Formula TEXT(). (Which is the Excel equivalent of VBA.Format. This can be accessed like So:
Sub Example()
MsgBox Excel.WorksheetFunction.Text(.3333,"# ?/?")
End Sub
To show more than one digit (Example 5/12) just up the number of question marks.
Google for "decimal to fraction" and you'll get about a gazillion results.
I really like this one, because it's simple, has source code (in RPL, similar to Forth, ~25 lines), and is pretty fast (it's written to run on a 4-bit, 4MHz CPU). The docs say:
In a book called Textbook of Algebra by G. Chrystal, 1st
edition in 1889, in Part II, Chapter 32, this improved continued fraction
algorithm is presented and proven. Odd to tell, Chrystal speaks of it as if it
were ancient knowledge.
This site seem to have a really nice implementation of this in JavaScript.
I would multiply by 10000000(or whatever you want depending on the precision), then simplify the resulting fraction (ie n*10000000/10000000)
You can approximate it. Essentially cycle through all numerators and denominators until you reach a fraction that is close to what you want.
int num = 1;
int den = 1;
double limit == 0.1;
double fraction = num / den;
while(den < 1000000 ) // some arbitrary large denominator
{
den = den + 1;
for(num = 0; num <= den; num++)
{
fraction = num / den;
if(fraction < n + limit && fraction > n - limit)
return (num + "/" + den);
}
}
This is slow and a brute force algorithm, but you should get the general idea.
In general, it'll be easier if you find the repeating part of the rational number. If you can't find that, you'll have a tough time. Let's say the number if 8.45735735735...
The answer is 8 + 45/100 + 735/999/100 = 8 1523/3330.
The whole number is 8.
Add 45/100 - which is .45, the part before the repeating part.
The repeating part is 735/999. In general, take the repeating part. Make it the numerator. The denominator is 10^(number of repeating digits) - 1.
Take the repeating part and shift it the appropriate number of digits. In this case, two, which means divide by 100, so 735/999/100.
Once you figure those parts out, you just need some code that adds and reduces fractions using greatest common fractions ...
Similar to CookieOfFortune's, but it's in VB and doesn't use as much brute force.
Dim tolerance As Double = 0.1 'Fraction has to be at least this close'
Dim decimalValue As Double = 0.125 'Original value to convert'
Dim highestDenominator = 100 'Highest denominator you`re willing to accept'
For denominator As Integer = 2 To highestDenominator - 1
'Find the closest numerator'
Dim numerator As Integer = Math.Round(denominator * decimalValue)
'Check if the fraction`s close enough'
If Abs(numerator / denominator - decimalValue) <= tolerance Then
Return numerator & "/" & denominator
End If
Next
'Didn't find one. Use the highest possible denominator'
Return Math.Round(denominator * decimalValue) & "/" & highestDenominator
...Let me know if it needs to account for values greater than 1, and I can adjust it.
EDIT: Sorry for the goofed up syntax highlighting. I can't figure out why it's all wrong. If someone knows how I can make it better, please let me know.
Python has a nice routine in its fractions module. Here is the working portion that converts a n/d into the closest approximation N/D where D <= some maximum value. e.g. if you want to find the closest fraction to 0.347, let n=347,d=1000 and max_denominator be 100 and you will obtain (17, 49) which is as close as you can get for denominators less than or equal to 100. The '//' operator is integer division so that 2//3 gives 0, i.e. a//b = int(a/b).
def approxFrac(n,d,max_denominator):
#give a representation of n/d as N/D where D<=max_denominator
#from python 2.6 fractions.py
#
# reduce by gcd and only run algorithm if d>maxdenominator
g, b = n, d
while b:
g, b = b, g%b
n, d = n/g, d/g
if d <= max_denominator:
return (n,d)
nn, dd = n, d
p0, q0, p1, q1 = 0, 1, 1, 0
while True:
a = nn//dd
q2 = q0+a*q1
if q2 > max_denominator:
break
p0, q0, p1, q1 = p1, q1, p0+a*p1, q2
nn, dd = dd, nn-a*dd
k = (max_denominator-q0)//q1
bound1 = (p0+k*p1, q0+k*q1)
bound2 = (p1, q1)
if abs(bound2[0]*d - bound2[1]*n) <= abs(bound1[0]*d - bound1[1]*n):
return bound2
else:
return bound1
1/ .3333333333 = 3 because 1/3 = .3333333333333, so whatever number you get do this,
double x = 1 / yourDecimal;
int y = Math.Ceil(x);
and now Display "1/" + y
It is not allways resoluble, since not all decimals are fractions (for example PI or e).
Also, you have to round up to some length your decimal before converting.
I know this is an old thread, but I came across this problem in Word VBA. There are so many limitations due to the 8 bit (16 digit) rounding, as well as Word VBA making decimals into scientific notation etc.. but after working around all these problems, I have a nice function I'd like to share that offers a few extra features you may find helpful.
The strategy is along the lines of what Daniel Buckner wrote. Basically:
1st) decide if it's a terminating decimal or not
2nd) If yes, just set the decimal tail / 10^n and reduce the fraction.
3rd) If it doesn't terminate, try to find a repeating pattern including cases where the repetition doesn't start right away
Before I post the function, here are a few of my observations of the risks and limitations, as well as some notes that may help you understand my approach.
Risks, limitations, explanations:
-> Optional parameter "denom" allows you to specify the denominator of the fraction, if you'd like it rounded. i.e. for inches you may want 16ths used. The fractions will still be reduced, however, so 3.746 --> 3 12/16 --> 3 3/4
-> Optional parameter "buildup" set to True will build up the fraction using the equation editor, typing the text right into the active document. If you prefer to have the function simply return a flat string representation of the fraction so you can store it programmatically etc. set this to False.
-> A decimal could terminate after a bunch of repetitions... this function would assume an infinite repetition.
-> Variable type Double trades off whole number digit for decimal digits, only allowing 16 digits total (from my observations anyway!). This function assumes that if a number is using all 16 of the available digits then it must be a repeating decimal. A large number such as 123456789876.25 would be mistaken for a repeating decimal, then returned as decimal number upon failing to find a pattern.
-> To express really large terminating decimal out of 10^n, VB can only handle 10^8 is seems. I round the origninal number to 8 decimal places, losing some accuracy perhaps.
-> For the math behind converting repeating patterns to fractions check this link
-> Use Euclidean Algorithm to reduce the fraction
Ok, here it is, written as a Word Macro:
Function as_fraction(number_, Optional denom As Integer = -1, Optional buildup As Boolean = True) As String
'Selection.TypeText Text:="Received: " & CStr(number_) & vbCrLf
Dim number As Double
Dim repeat_digits As Integer, delay_digits As Integer, E_position As Integer, exponent As Integer
Dim tail_string_test As String, tail_string_original As String, num_removed As String, tail_string_removed As String, removed As String, num As String, output As String
output = "" 'string variable to build into the fraction answer
number = CDbl(number_)
'Get rid of scientific notation since this makes the string longer, fooling the function length = digits
If InStr(CStr(number_), "E+") > 0 Then 'no gigantic numbers! Return that scientific notation junk
output = CStr(number_)
GoTo all_done
End If
E_position = InStr(CStr(number), "E") 'E- since postives were handled
If E_position > 0 Then
exponent = Abs(CInt(Mid(CStr(number), E_position + 1)))
num = Mid(CStr(number_), 1, E_position) 'axe the exponent
decimalposition = InStr(num, ".") 'note the decimal position
For i_move = 1 To exponent
'move the decimal over, and insert a zero if the start of the number is reached
If InStr(num, "-") > 0 And decimalposition = 3 Then 'negative sign in front
num = "-0." & Mid(num, InStr(num, ".") - 1, 1) & Mid(num, InStr(num, ".") + 1) 'insert a zero after the negative
ElseIf decimalposition = 2 Then
num = "0." & Mid(num, InStr(num, ".") - 1, 1) & Mid(num, InStr(num, ".") + 1) 'insert in front
Else 'move the decimal over, there are digits left
num = Mid(num, 1, decimalposition - 2) & "." & Mid(num, decimalposition - 1, 1) & Mid(num, decimalposition + 1)
decimalposition = decimalposition - 1
End If
Next
Else
num = CStr(number_)
End If
'trim the digits to 15, since VB rounds the last digit which ruins the pattern. i.e. 0.5555555555555556 etc.
If Len(num) >= 16 Then
num = Mid(num, 1, 15)
End If
number = CDbl(num) 'num is a string representation of the decimal number, just to avoid cstr() everywhere
'Selection.TypeText Text:="number = " & CStr(number) & vbCrLf
'is it a whole number?
If Fix(number) = number Then 'whole number
output = CStr(number)
GoTo all_done
End If
decimalposition = InStr(CStr(num), ".")
'Selection.TypeText Text:="Attempting to find a fraction equivalent for " & num & vbCrLf
'is it a repeating decimal? It will have 16 digits
If denom = -1 And Len(num) >= 15 Then 'repeating decimal, unspecified denominator
tail_string_original = Mid(num, decimalposition + 1) 'digits after the decimal
delay_digits = -1 'the number of decimal place values removed from the tail, in case the repetition is delayed. i.e. 0.567777777...
Do 'loop through start points for the repeating digits
delay_digits = delay_digits + 1
If delay_digits >= Fix(Len(tail_string_original) / 2) Then
'Selection.TypeText Text:="Tried all starting points for the pattern, up to half way through the tail. None was found. I'll treat it as a terminating decimal." & vbCrLf
GoTo treat_as_terminating
End If
num_removed = Mid(num, 1, decimalposition) & Mid(num, decimalposition + 1 + delay_digits) 'original number with decimal values removed
tail_string_removed = Mid(num_removed, InStr(CStr(num_removed), ".") + 1)
repeat_digits = 0 'exponent on 10 for moving the decimal place over
'Selection.TypeText Text:="Searching " & num_removed & " for a pattern:" & vbCrLf
Do
repeat_digits = repeat_digits + 1
If repeat_digits = Len(tail_string_removed) - 1 Or repeat_digits >= 9 Then 'try removing a digit, incase the pattern is delayed
Exit Do
End If
tail_string_test = Mid(num_removed, decimalposition + 1 + repeat_digits)
'Selection.TypeText Text:=vbTab & "Comparing " & Mid(tail_string_removed, 1, Len(tail_string_removed) - repeat_digits) & " to " & tail_string_test & vbCrLf
If Mid(tail_string_removed, 1, Len(tail_string_removed) - repeat_digits) = tail_string_test Then
'Selection.TypeText Text:=num & ", " & Mid(tail_string_removed, 1, Len(tail_string_removed) - repeat_digits) & " vs " & tail_string_test & vbCrLf
GoTo foundpattern
End If
Loop
Loop 'next starting point for pattern
foundpattern:
If delay_digits = 0 Then 'found pattern right away
numerator = CLng(Mid(CStr(number), decimalposition + 1 + delay_digits, CInt(repeat_digits)))
'generate the denominator nines, same number of digits as the numerator
bottom = ""
For i_loop = 1 To repeat_digits
bottom = bottom & "9"
Next
denominator = CLng(bottom)
Else 'there were numbers before the pattern began
numerator = CLng(Mid(num, decimalposition + 1, delay_digits + repeat_digits)) - CLng(Mid(num, decimalposition + 1, delay_digits))
'i.e. x = 2.73232323232... delay_digits = 1, repeat_digits = 2, so numerator = 732 - 7 = 725
bottom = ""
For i_loop = 1 To repeat_digits
bottom = bottom & "9"
Next
For i_loop = 1 To delay_digits
bottom = bottom & "0"
Next
denominator = CLng(bottom)
'i.e. 990... 725/990 = 145/198 = 0.7323232...
End If
Else ' terminating decimal
treat_as_terminating:
'grab just the decimal trail
If denom = -1 Then
number = Math.Round(number, 8) 'reduce to fewer decimal places to avoid overload
'is it a whole number now?
If Fix(number) = number Then 'whole number
output = CStr(number)
GoTo all_done
End If
num = CStr(number)
numerator = CLng(Mid(num, decimalposition + 1))
denominator = 10 ^ (Len(num) - InStr(num, "."))
Else 'express as a fraction rounded to the nearest denom'th reduced
numerator1 = CDbl("0" & Mid(CStr(num), decimalposition))
numerator = CInt(Math.Round(numerator1 * denom))
denominator = CInt(denom)
End If
End If
'reduce the fraction if possible using Euclidean Algorithm
a = CLng(numerator)
b = CLng(denominator)
Dim t As Long
Do While b <> 0
t = b
b = a Mod b
a = t
Loop
gcd_ = a
numerator = numerator / gcd_
denominator = denominator / gcd_
whole_part = CLng(Mid(num, 1, decimalposition - 1))
'only write a whole number if the number is absolutely greater than zero, or will round to be so.
If whole_part <> 0 Or (whole_part = 0 And numerator = denominator) Then
'case where fraction rounds to whole
If numerator = denominator Then
'increase the whole by 1 absolutely
whole_part = (whole_part / Abs(whole_part)) * (Abs(whole_part) + 1)
End If
output = CStr(whole_part) & " "
End If
'if fraction rounded to a whole, it is already included in the whole number
If numerator <> 0 And numerator <> denominator Then
'negative sign may have been missed, if whole number was -0
If whole_part = 0 And number_ < 0 Then
numerator = -numerator
End If
output = output & CStr(numerator) & "/" & CStr(denominator) & " "
End If
If whole_part = 0 And numerator = 0 Then
output = "0"
End If
all_done:
If buildup = True Then 'build up the equation with a pretty fraction at the current selection range
Dim objRange As Range
Dim objEq As OMath
Dim AC As OMathAutoCorrectEntry
Application.OMathAutoCorrect.UseOutsideOMath = True
Set objRange = Selection.Range
objRange.Text = output
For Each AC In Application.OMathAutoCorrect.Entries
With objRange
If InStr(.Text, AC.Name) > 0 Then
.Text = Replace(.Text, AC.Name, AC.Value)
End If
End With
Next AC
Set objRange = Selection.OMaths.Add(objRange)
Set objEq = objRange.OMaths(1)
objEq.buildup
'Place the cursor at the end of the equation, outside of the OMaths object
objRange.OMaths(1).Range.Select
Selection.Collapse direction:=wdCollapseEnd
Selection.MoveRight Unit:=wdCharacter, count:=1
as_fraction = "" 'just a dummy return to make the function happy
Else 'just return a flat string value
as_fraction = output
End If
End Function
I shared an answer at this link : https://stackoverflow.com/a/57517128/11933717
It's also an iterative function, but unlike finding numerator and denominator in a nested loop, it just tests numerators only and so, should be faster.
Here is how it works :
It assumes that, based on the user input x, you want to find 2 integers n / m .
n/m = x , meaning that
n/x should give an almost integer m
Say one needs to find a fraction for x = 2.428571. Putting the int 2 aside for later, the algo starts by setting n and x and iterates n :
// n / x = m ( we need m to be an integer )
// n = 1 ; x = .428571 ;
1 / .428571 = 2.333335 (not close to an integer, n++)
2 / .428571 = 4.666671 (not close to an integer, n++)
3 / .428571 = 7.000007
At this point n = 3, we consider that m = 7.000007 is integer enough --based on some kind of accuracy the programmer decides-- and we reply the user
2.428571 = 2 + 3/7
= 14/7 + 3/7
= 17/7