Sign in to follow this  
Followers 0
YellowLab

Adding Fractions

4 posts in this topic

I had a need to add fractions and display them as strings and couldn't find a function to do that. So, I created one and thought I would share it.

Input are two "strings" representing the fractions. Valid inputs are integers or strings in the following formats: "#", "#/#" or "# #/#"

These formats were chosen because they work for my script.

Output is either a two row array in the form of [Numerator, Denominator] or a string in the same format as the inputs.

The function uses PSaltyDS's Factor function found here to reduce the fraction to its lowest denominator.

_ArraySearch and _ArrayDelete are also used from Array.au3 to assist in the reduced fraction calculation.

"Fractions.au3"

CODE

#include-once

#include <Array.au3>

;input $sFraction1 and $sFraction2 can either be a string "#", "#/#", "# #/#" or an Int

;input $Result can either be "ARRAY" for the result to be returned as a two row array, [Numerator, Denominator]

;or anything else to return it as a string "#", "#/#", "# #/#"

;@error = 1, input incorrectly formated, extended=1->first, extended=2->second

;@error = 2, multiple /'s, extended=1->first, extended=2->second

Func _AddFractions($sFraction1, $sFraction2, $Result="STRING")

Local $nNum1, $nDen1, $nNum2, $nDen2, $nNumSum, $nDenSum, $nIndex

Local $arTemp, $arFNum, $arFDen

;Test for valid input (integers or strings)

If NOT IsInt($sFraction1) Then

If IsString($sFraction1) Then

If NOT IsInt(Number($sFraction1)) Then

If StringInStr($sFraction1,"/") = -1 Then

SetError(1,1)

Return -1

EndIf

Else

;check for zero

If $sFraction1<>"0" And Number($sFraction1)=0 Then

SetError(1,1)

Return -1

EndIf

EndIf

Else

;not a string, not an int

SetError(1,1)

Return -1

EndIf

EndIf

If Not IsInt($sFraction2) Then

If IsString($sFraction2) Then

If NOT IsInt(Number($sFraction2)) Then

If StringInStr($sFraction2,"/") = -1 Then

SetError(1,2)

Return -1

EndIf

Else

;check for zero

If $sFraction2<>"0" And Number($sFraction2)=0 Then

SetError(1,2)

Return -1

EndIf

EndIf

Else

;not a string, not an int

SetError(1,2)

Return -1

EndIf

EndIf

If IsInt($sFraction1) Then

$nNum1=$sFraction1

$nDen1=1

Else

$arTemp=StringSplit($sFraction1,'/')

If $arTemp[0] > 2 Then

SetError(2,1)

Return -1

ElseIf $arTemp[0]=1 Then

$nNum1=$arTemp[1]

$nDen1=1

Else

$nNum1=$arTemp[1]

$nDen1=$arTemp[2]

$arTemp=StringSplit($nNum1," ")

If $arTemp[0]=2 Then

$nNum1=$arTemp[1]*$nDen1+$arTemp[2]

EndIf

EndIf

EndIf

If IsInt($sFraction2) Then

$nNum2=$sFraction2

$nDen2=1

Else

$arTemp=StringSplit($sFraction2,'/')

If $arTemp[0] > 2 Then

SetError(2,2)

Return -1

ElseIf $arTemp[0]=1 Then

$nNum2=$arTemp[1]

$nDen2=1

Else

$nNum2=$arTemp[1]

$nDen2=$arTemp[2]

$arTemp=StringSplit($nNum2," ")

If $arTemp[0]=2 Then

$nNum2=$arTemp[1]*$nDen2+$arTemp[2]

EndIf

EndIf

EndIf

$nNumSum=$nNum1*$nDen2+$nNum2*$nDen1

$nDenSum=$nDen1*$nDen2

;reduce fraction

$arFNum=_Factor($nNumSum)

$arFDen=_Factor($nDenSum)

If $arFNum[0] < $arFDen[0] Then

For $i=2 To UBound($arFNum)-1

$nIndex=_ArraySearch($arFDen,$arFNum[$i])

If $nIndex>1 Then

_ArrayDelete($arFDen,$nIndex)

$nNumSum=$nNumSum/$arFNum[$i]

$nDenSum=$nDenSum/$arFNum[$i]

EndIf

Next

Else

For $i=2 To Ubound($arFDen)-1

$nIndex=_ArraySearch($arFNum,$arFDen[$i])

If $nIndex>1 Then

_ArrayDelete($arFNum,$nIndex)

$nNumSum=$nNumSum/$arFDen[$i]

$nDenSum=$nDenSum/$arFDen[$i]

EndIf

Next

EndIf

If $Result="ARRAY" Then

Dim $arTemp[2]=[$nNumSum,$nDenSum]

Return $arTemp

Else

Local $sTemp

If Floor($nNumSum/$nDenSum)=0 Then

$sTemp=$nNumSum&'/'&$nDenSum

Else

$sTemp=Floor($nNumSum/$nDenSum)&' '&Mod($nNumSum,$nDenSum)&'/'&$nDenSum

EndIf

Return $sTemp

EndIf

EndFunc ;==>_AddFractions

;http://www.autoitscript.com/forum/index.php?showtopic=57541&st=15&p=455305&#entry455305

; ----------------------------------------------------------------------

; Function: _Factor()

; Purpose: Return all factors of a signed integer

; Syntax: _Factor($i)

; Where: $i = signed integer value

; Returns: A 1D array with [0] = count of factors

; On success: [1] = 1 or -1 depending on sign of input $i, [2] thru [n] = prime factors

; On failure: [0] = 0 and sets @error

; Notes: Based on the simplest "naive" prime number test, sped up some by inclusion

; of a short prime number table.

; The number 1 is not normally given as a prime factor, but is included so

; the function can handle signed/unsigned numbers in a predictable manner.

; Element [1] of the array will be 1 or -1 depending on the sign of the input.

; Returns @error for non-integer or 0 inputs.

; Author: PsaltyDS at www.autoitscript.com/forum

; Version: 1.0.0 dated 28 December, 2007

; ----------------------------------------------------------------------

Func _Factor($i)

Local $avRET[1] = [0]

Local $iTest, $iWorking, $iOddNum

; Test for valid input

If IsInt($i) = 0 Then

SetError(1)

Return $avRET

ElseIf $i = 0 Then

SetError(2)

Return $avRET

EndIf

; Check for negative number

Local $avRET[1024] = [1, 1]

If $i < 0 Then

$avRET[1] = -1

$i = $i * - 1

EndIf

$iWorking = $i

; Quick return for input = 1

If $iWorking = 1 Then

ReDim $avRET[2]

Return $avRET

EndIf

; Initial table of primes

Local $avPrimes[201] = [200, _

2, 3, 5, 7, 11, 13, 17, 19, 23, 29, _

31, 37, 41, 43, 47, 53, 59, 61, 67, 71, _

73, 79, 83, 89, 97, 101, 103, 107, 109, 113, _

127, 131, 137, 139, 149, 151, 157, 163, 167, 173, _

179, 181, 191, 193, 197, 199, 211, 223, 227, 229, _

233, 239, 241, 251, 257, 263, 269, 271, 277, 281, _

283, 293, 307, 311, 313, 317, 331, 337, 347, 349, _

353, 359, 367, 373, 379, 383, 389, 397, 401, 409, _

419, 421, 431, 433, 439, 443, 449, 457, 461, 463, _

467, 479, 487, 491, 499, 503, 509, 521, 523, 541, _

547, 557, 563, 569, 571, 577, 587, 593, 599, 601, _

607, 613, 617, 619, 631, 641, 643, 647, 653, 659, _

661, 673, 677, 683, 691, 701, 709, 719, 727, 733, _

739, 743, 751, 757, 761, 769, 773, 787, 797, 809, _

811, 821, 823, 827, 829, 839, 853, 857, 859, 863, _

877, 881, 883, 887, 907, 911, 919, 929, 937, 941, _

947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, _

1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, _

1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, _

1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223]

; Use static table of primes to start with

For $p = 1 To $avPrimes[0]

While 1

$iTest = Mod($iWorking, $avPrimes[$p])

If $iTest = 0 Then

; This is a factor

$avRET[0] += 1

$avRET[$avRET[0]] = $avPrimes[$p]

$iWorking = $iWorking / $avPrimes[$p]

If $iWorking = 1 Then

; Last factor found

ExitLoop 2

Else

; Try same prime again

ContinueLoop

EndIf

Else

; This is not a factor, next prime

ExitLoop

EndIf

WEnd

Next

; Continue if remaining factors are larger than the static primes

If $iWorking > $avPrimes[$avPrimes[0]] Then

$iOddNum = $avPrimes[$avPrimes[0]] + 2

While 1

While 1

$iTest = Mod($iWorking, $iOddNum)

If $iTest = 0 Then

; This is a factor

$avRET[0] += 1

$avRET[$avRET[0]] = $iOddNum

$iWorking = $iWorking / $iOddNum

If $iWorking = 1 Then

; Last factor found

ExitLoop 2

Else

; Try same odd number again

ContinueLoop

EndIf

Else

; This not a factor, next odd number

ExitLoop

EndIf

WEnd

$iOddNum += 2

If $iOddNum > Int(Sqrt($i)) Then

; Remaining number is prime = last factor

$avRET[0] += 1

$avRET[$avRET[0]] = $iWorking

ExitLoop

EndIf

WEnd

EndIf

; Resize the array and return

ReDim $avRET[$avRET[0] + 1]

Return $avRET

EndFunc ;==>_Factor

testfractions.au3

CODE

#include "Fractions.au3"

$nTemp1=_AddFractions("1/16", "3/4")

$nTemp2=_AddFractions("3 21/43","5 12/103")

$nTemp3=_AddFractions(3,"2/14")

$nTemp4=_AddFractions("3","7/59")

$nTemp5=_AddFractions(4,5)

$nTemp6=_AddFractions("9/16","9/16","ARRAY")

;to reduce a fraction, just add zero

$nTemp7=_AddFractions("33/99",0)

ConsoleWrite('1/16 + 3/4 = '&$nTemp1&@crlf& _

'3 21/43 + 5 12/103 = '&$nTemp2&@CRLF& _

'3 (int) + 2/14 = '&$nTemp3&@CRLF& _

'3 + 7/59 = '&$nTemp4&@CRLF& _

'4 (int) + 5 (int) = '&$nTemp5&@CRLF& _

'Reduce 33/99 => '&$nTemp7&@CRLF)

_ArrayDisplay($nTemp6)

Bob


You can't see a rainbow without first experiencing the rain.

Share this post


Link to post
Share on other sites



Cool, now it just needs a GUI to plug in variables and your good to go.. Nice start..

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

Just a quick little update to handle float values. If one (or both) of the inputs are type Float, then the resultant sum will be returned as a type Float.

"fractions.au3"

CODE

;input $sFraction1 and $sFraction2 can either be a string "#", "#/#", "# #/#" or an Int or Float

;input $Result can either be "ARRAY" for the result to be returned as a two row array, [Numerator, Denominator]

;or anything else to return it as a string "#", "#/#", "# #/#"

;if either input is a Float then the sum of the two is returned as a Float

;@error = 1, input incorrectly formated, extended=1->first, extended=2->second

;@error = 2, multiple /'s, extended=1->first, extended=2->second

Func _AddFractions($sFraction1, $sFraction2, $Result="STRING")

Local $nNum1, $nDen1, $nNum2, $nDen2, $nNumSum, $nDenSum, $nIndex

Local $arTemp, $arFNum, $arFDen

Local $b1Float=False, $b2Float=False

;Test for valid input (integers or strings)

If NOT IsInt($sFraction1) Then

If IsString($sFraction1) Then

If NOT IsInt(Number($sFraction1)) Then

If StringInStr($sFraction1,"/") = -1 Then

SetError(1,1)

Return -1

EndIf

Else

;check for zero

If $sFraction1<>"0" And Number($sFraction1)=0 Then

SetError(1,1)

Return -1

EndIf

EndIf

Else

If IsFloat($sFraction1) Then

$b1Float=True

Else

;not a string, not an int

SetError(1,1)

Return -1

EndIf

EndIf

EndIf

If Not IsInt($sFraction2) Then

If IsString($sFraction2) Then

If NOT IsInt(Number($sFraction2)) Then

If StringInStr($sFraction2,"/") = -1 Then

SetError(1,2)

Return -1

EndIf

Else

;check for zero

If $sFraction2<>"0" And Number($sFraction2)=0 Then

SetError(1,2)

Return -1

EndIf

EndIf

Else

If IsFloat($sFraction2) Then

$b2Float=True

Else

;not a string, not an int

SetError(1,2)

Return -1

EndIf

EndIf

EndIf

;If $sFraction1 or $sFraction2 is a floating point return the floating point addendum

If BitOr($b1Float,$b2Float) Then

If IsFloat($sFraction1) AND IsFloat($sFraction2) Then Return $sFraction1+$sFraction2

If IsFloat($sFraction1) Then

$arTemp=_StringToNumDen($sFraction2)

Return $sFraction1+$arTemp[0]/$arTemp[1]

Else

$arTemp=_StringToNumDen($sFraction1)

Return $sFraction2+$arTemp[0]/$arTemp[1]

EndIf

EndIf

$arTemp=_StringToNumDen($sFraction1)

If $arTemp=-1 Then

SetError(2,1)

Return -1

Else

$nNum1=$arTemp[0]

$nDen1=$arTemp[1]

EndIf

$arTemp=_StringToNumDen($sFraction2)

If $arTemp=-1 Then

SetError(2,2)

Return -1

Else

$nNum2=$arTemp[0]

$nDen2=$arTemp[1]

EndIf

$nNumSum=$nNum1*$nDen2+$nNum2*$nDen1

$nDenSum=$nDen1*$nDen2

;reduce fraction

$arFNum=_Factor($nNumSum)

$arFDen=_Factor($nDenSum)

If $arFNum[0] < $arFDen[0] Then

For $i=2 To UBound($arFNum)-1

$nIndex=_ArraySearch($arFDen,$arFNum[$i])

If $nIndex>1 Then

_ArrayDelete($arFDen,$nIndex)

$nNumSum=$nNumSum/$arFNum[$i]

$nDenSum=$nDenSum/$arFNum[$i]

EndIf

Next

Else

For $i=2 To Ubound($arFDen)-1

$nIndex=_ArraySearch($arFNum,$arFDen[$i])

If $nIndex>1 Then

_ArrayDelete($arFNum,$nIndex)

$nNumSum=$nNumSum/$arFDen[$i]

$nDenSum=$nDenSum/$arFDen[$i]

EndIf

Next

EndIf

If $Result="ARRAY" Then

Dim $arTemp[2]=[$nNumSum,$nDenSum]

Return $arTemp

Else

Local $sTemp

If Floor($nNumSum/$nDenSum)=0 Then

$sTemp=$nNumSum&'/'&$nDenSum

Else

If Mod($nNumSum,$nDenSum) = 0 Then

$sTemp=Floor($nNumSum/$nDenSum)

Else

$sTemp=Floor($nNumSum/$nDenSum)&' '&Mod($nNumSum,$nDenSum)&'/'&$nDenSum

EndIf

EndIf

Return $sTemp

EndIf

EndFunc ;==>_AddFractions

;converts String to 2 element array - 1st is numerator, 2nd is denominator, -1 is fail

Func _StringToNumDen($sFraction1)

Local $nDen1, $nNum1, $arTemp

If IsInt($sFraction1) Then

$nNum1=$sFraction1

$nDen1=1

Else

$arTemp=StringSplit($sFraction1,'/')

If $arTemp[0] > 2 Then

Return -1

ElseIf $arTemp[0]=1 Then

$nNum1=$arTemp[1]

$nDen1=1

Else

$nNum1=$arTemp[1]

$nDen1=$arTemp[2]

$arTemp=StringSplit($nNum1," ")

If $arTemp[0]=2 Then

$nNum1=$arTemp[1]*$nDen1+$arTemp[2]

EndIf

EndIf

EndIf

Dim $arTemp[2]=[$nNum1,$nDen1]

Return $arTemp

EndFunc ;==>_StringToNumDen

;http://www.autoitscript.com/forum/index.php?showtopic=57541&st=15&p=455305&#entry455305

; ----------------------------------------------------------------------

; Function: _Factor()

; Purpose: Return all factors of a signed integer

; Syntax: _Factor($i)

; Where: $i = signed integer value

; Returns: A 1D array with [0] = count of factors

; On success: [1] = 1 or -1 depending on sign of input $i, [2] thru [n] = prime factors

; On failure: [0] = 0 and sets @error

; Notes: Based on the simplest "naive" prime number test, sped up some by inclusion

; of a short prime number table.

; The number 1 is not normally given as a prime factor, but is included so

; the function can handle signed/unsigned numbers in a predictable manner.

; Element [1] of the array will be 1 or -1 depending on the sign of the input.

; Returns @error for non-integer or 0 inputs.

; Author: PsaltyDS at www.autoitscript.com/forum

; Version: 1.0.0 dated 28 December, 2007

; ----------------------------------------------------------------------

Func _Factor($i)

Local $avRET[1] = [0]

Local $iTest, $iWorking, $iOddNum

; Test for valid input

If IsInt($i) = 0 Then

SetError(1)

Return $avRET

ElseIf $i = 0 Then

SetError(2)

Return $avRET

EndIf

; Check for negative number

Local $avRET[1024] = [1, 1]

If $i < 0 Then

$avRET[1] = -1

$i = $i * - 1

EndIf

$iWorking = $i

; Quick return for input = 1

If $iWorking = 1 Then

ReDim $avRET[2]

Return $avRET

EndIf

; Initial table of primes

Local $avPrimes[201] = [200, _

2, 3, 5, 7, 11, 13, 17, 19, 23, 29, _

31, 37, 41, 43, 47, 53, 59, 61, 67, 71, _

73, 79, 83, 89, 97, 101, 103, 107, 109, 113, _

127, 131, 137, 139, 149, 151, 157, 163, 167, 173, _

179, 181, 191, 193, 197, 199, 211, 223, 227, 229, _

233, 239, 241, 251, 257, 263, 269, 271, 277, 281, _

283, 293, 307, 311, 313, 317, 331, 337, 347, 349, _

353, 359, 367, 373, 379, 383, 389, 397, 401, 409, _

419, 421, 431, 433, 439, 443, 449, 457, 461, 463, _

467, 479, 487, 491, 499, 503, 509, 521, 523, 541, _

547, 557, 563, 569, 571, 577, 587, 593, 599, 601, _

607, 613, 617, 619, 631, 641, 643, 647, 653, 659, _

661, 673, 677, 683, 691, 701, 709, 719, 727, 733, _

739, 743, 751, 757, 761, 769, 773, 787, 797, 809, _

811, 821, 823, 827, 829, 839, 853, 857, 859, 863, _

877, 881, 883, 887, 907, 911, 919, 929, 937, 941, _

947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, _

1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, _

1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, _

1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223]

; Use static table of primes to start with

For $p = 1 To $avPrimes[0]

While 1

$iTest = Mod($iWorking, $avPrimes[$p])

If $iTest = 0 Then

; This is a factor

$avRET[0] += 1

$avRET[$avRET[0]] = $avPrimes[$p]

$iWorking = $iWorking / $avPrimes[$p]

If $iWorking = 1 Then

; Last factor found

ExitLoop 2

Else

; Try same prime again

ContinueLoop

EndIf

Else

; This is not a factor, next prime

ExitLoop

EndIf

WEnd

Next

; Continue if remaining factors are larger than the static primes

If $iWorking > $avPrimes[$avPrimes[0]] Then

$iOddNum = $avPrimes[$avPrimes[0]] + 2

While 1

While 1

$iTest = Mod($iWorking, $iOddNum)

If $iTest = 0 Then

; This is a factor

$avRET[0] += 1

$avRET[$avRET[0]] = $iOddNum

$iWorking = $iWorking / $iOddNum

If $iWorking = 1 Then

; Last factor found

ExitLoop 2

Else

; Try same odd number again

ContinueLoop

EndIf

Else

; This not a factor, next odd number

ExitLoop

EndIf

WEnd

$iOddNum += 2

If $iOddNum > Int(Sqrt($i)) Then

; Remaining number is prime = last factor

$avRET[0] += 1

$avRET[$avRET[0]] = $iWorking

ExitLoop

EndIf

WEnd

EndIf

; Resize the array and return

ReDim $avRET[$avRET[0] + 1]

Return $avRET

EndFunc ;==>_Factor

"fractiontest.au3"

CODE

#include "Fractions.au3"

$nTemp1=_AddFractions("1/16", "3/4")

$nTemp2=_AddFractions("3 21/43",.25)

$nTemp3=_AddFractions(3,"2/14")

$nTemp4=_AddFractions("3","7/59")

$nTemp5=_AddFractions(4,5)

$nTemp6=_AddFractions("9/16","9/16","ARRAY")

;to reduce a fraction, just add zero

$nTemp7=_AddFractions("33/99",0)

ConsoleWrite('1/16 + 3/4 = '&$nTemp1&@crlf& _

'3 21/43 + 5 12/103 = '&$nTemp2&@CRLF& _

'3 (int) + 2/14 = '&$nTemp3&@CRLF& _

'3 + 7/59 = '&$nTemp4&@CRLF& _

'4 (int) + 5 (int) = '&$nTemp5&@CRLF& _

'Reduce 33/99 => '&$nTemp7&@CRLF)

_ArrayDisplay($nTemp6)

Bob

Edited by YellowLab

You can't see a rainbow without first experiencing the rain.

Share this post


Link to post
Share on other sites

Nice work.

I was wondering if that same method could be used for a basic arithmetic script?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0