Page 1 of 1

Mod function returning wrong value?

Posted: 28 Mar 2017, 06:39
by jeeswg

Code: Select all

q:: ;Mod issue? tested on AutoHotkeyU32.exe v1.1.25.01
MsgBox % Mod(12.345, 0.01) ;0.005000 (correct, as expected)
MsgBox % Mod(12.34, 0.01) ;0.010000 (bug?) (expected 0)
MsgBox % Mod(1.23, 0.01) ;0.010000 (bug?) (expected 0)
return
see: http://www.wolframalpha.com/input/?i=Mod(12.34,+0.01)
see: http://www.wolframalpha.com/input/?i=Mod(1.23,+0.01)

- Is this related to mathematics being done in hex/bin but rounded and displayed in dec?
- Should the documentation state a specific range in which Mod is correct?

Math Functions
https://autohotkey.com/docs/commands/Math.htm#Mod
Functions
https://autohotkey.com/docs/Functions.htm#Mod

Re: Mod function returning wrong value?

Posted: 28 Mar 2017, 07:02
by jNizM
This one works...

Code: Select all

modulo(x, y)
{
    return x - ((x // y) * y)
}

MsgBox % modulo(12.345, 0.01)    ; -> 0.005000
MsgBox % modulo(12.34, 0.01)     ; -> 0.000000
MsgBox % modulo(1.23, 0.01)      ; -> 0.000000

Re: Mod function returning wrong value?

Posted: 28 Mar 2017, 07:48
by jeeswg
Thanks. I came across this issue when looking at truncating/rounding numbers.

compare numbers with different decimal point - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 75#p138275

You've prompted me to write this function, where you can optionally choose whether the output is above/below zero.
[EDIT: ignore this function, see post lower down]

Code: Select all

;vMode: 1/0/-1, return pos/sign of dividend/neg
;note: assumes vDivisor is positive
JEE_Mod(vDividend, vDivisor, vMode=0)
{
	if (vDivisor = 0)
		return
	(vMode = 0) ? (vMode := vDividend) : ""
	vQuotient := Floor(vDividend / vDivisor)
	vRemainder := vDividend - (vQuotient * vDivisor)
	if (vRemainder < 0) && (vMode > 0)
		return vRemainder + vDivisor
	if (vRemainder > 0) && (vMode < 0)
		return vRemainder - vDivisor
	return vRemainder
}
I haven't made sense yet of what to do with a negative divisor.

Btw ooh, almost 2000 posts!

Re: Mod function returning wrong value?

Posted: 28 Mar 2017, 08:36
by jNizM
qmath.h (AutoHotkey Source)

Code: Select all

#include <math.h>

// Inline assembly isn't available, so qmath is essentially disabled.

// ...
#define qmathFmod	fmod
// ...
The fmod functions compute the floating-point remainder of x/y and is not the same like the Euclidean division modulo.
fmod wrote:Returns the floating-point remainder of numer/denom (rounded towards zero):

fmod = numer - tquot * denom

Where tquot is the truncated (i.e., rounded towards zero) result of: numer/denom.

A similar function, remainder, returns the same but with the quotient rounded to the nearest integer (instead of truncated).
fmod, fmodf (msdn) - Calculates the floating-point remainder.

Code: Select all

MsgBox %         mod(12.345, 0.01)    ; -> 0.005000
       . "`n"   fmod(12.345, 0.01)    ; -> 0.005000
	   . "`n"  fmodf(12.345, 0.01)    ; -> 0.005001
	   . "`n" modulo(12.345, 0.01)    ; -> 0.005000

MsgBox %         mod(12.34, 0.01)     ; -> 0.010000
       . "`n"   fmod(12.34, 0.01)     ; -> 0.010000
	   . "`n"  fmodf(12.34, 0.01)     ; -> 0.010000
	   . "`n" modulo(12.34, 0.01)     ; -> 0.000000

MsgBox %         mod(1.23, 0.01)      ; -> 0.010000
       . "`n"   fmod(1.23, 0.01)      ; -> 0.010000
	   . "`n"  fmodf(1.23, 0.01)      ; -> 0.010000
	   . "`n" modulo(1.23, 0.01)      ; -> 0.000000

Code: Select all

modulo(x, y) {
    return x - ((x // y) * y)
}

fmod(x, y) {
    return DllCall("msvcrt\fmod", "double", x, "double", y, "cdecl double")
}

fmodf(x, y) {
    return DllCall("msvcrt\fmodf", "float", x, "float", y, "cdecl float")
}

Re: Mod function returning wrong value?

Posted: 28 Mar 2017, 10:17
by jeeswg
Very good work jNizM, I'm most grateful.

Re: Mod function returning wrong value?

Posted: 09 Apr 2017, 18:08
by jeeswg
JEE_Mod(vDividend, vDivisor, vMode:=0) ;mimics AutoHotkey (result is same sign as dividend)
JEE_ModWA(vDividend, vDivisor, vMode:=0) ;mimics wolframalpha.com (result is same sign as divisor)

Code: Select all

q:: ;test Mod function and alternative Mod functions
vFunc := "Mod"
;vFunc := "JEE_Mod"
;vFunc := "JEE_ModWA"
vMode := 0
;vMode := 1
;vMode := -1

;e.g. http://www.wolframalpha.com/input/?i=Mod(7,-4)
;3 is the result with AutoHotkey
;-1 is the result with wolframalpha.com

MsgBox, % %vFunc%(7,4,vMode) ;3 (WA: 3)
MsgBox, % %vFunc%(7,-4,vMode) ;3 (WA: -1)
MsgBox, % %vFunc%(-7,4,vMode) ;-3 (WA: 1)
MsgBox, % %vFunc%(-7,-4,vMode) ;-3 (WA: -3)

MsgBox, % %vFunc%(7,5,vMode) ;2 (WA: 2)
MsgBox, % %vFunc%(7,-5,vMode) ;2 (WA: -3)
MsgBox, % %vFunc%(-7,5,vMode) ;-2 (WA: 3)
MsgBox, % %vFunc%(-7,-5,vMode) ;-2 (WA: -2)
return

;==================================================

;mimics AutoHotkey
;vMode: 1/0/-1, result is positive/same sign as dividend/negative
JEE_Mod(vDividend, vDivisor, vMode:=0)
{
	if (vDivisor = 0)
		return
	vDividendOrig := vDividend
	vDividend := Abs(vDividend)
	vDivisor := Abs(vDivisor)
	vQuotient := Floor(vDividend / vDivisor)
	vRemainder := vDividend - (vQuotient * vDivisor)
	if (vDividendOrig < 0)
		vRemainder *= -1
	if (vRemainder < 0) && (vMode > 0)
		return vRemainder + vDivisor
	if (vRemainder > 0) && (vMode < 0)
		return vRemainder - vDivisor
	return vRemainder
}

;==================================================

;mimics wolframalpha.com
;vMode: 1/0/-1, result is positive/same sign as divisor/negative
JEE_ModWA(vDividend, vDivisor, vMode:=0)
{
	if (vDivisor = 0)
		return
	vQuotient := Floor(vDividend / vDivisor)
	vRemainder := vDividend - (vQuotient * vDivisor)
	if (vRemainder < 0) && (vMode > 0)
		return vRemainder + Abs(vDivisor)
	if (vRemainder > 0) && (vMode < 0)
		return vRemainder - Abs(vDivisor)
	return vRemainder
}

;==================================================

Re: Mod function returning wrong value?

Posted: 27 Oct 2019, 21:54
by aldrinjohnom
For future readers who are unaware of what is causing this bug. It is the floating numbers itself, when you operate with floating points, its 15th to 32th decimal places produces a random numbers which leads "mod(),float(),ceil(), floor division" to inaccurately do their computations when there is one or more floating numbers included on at their operations... Floating point issues existed ages ago until now.

This can be observed by using this sample code:

Code: Select all

x:=0.05+0.01,y:=0.95-0.05,z:=15.324-3
Setformat,float,0.32
msgbox %x%`n%y%`n%z%

Setformat,float,0.32
x:=0.05+0.01,y:=0.95-0.05,z:=15.324-3
msgbox %x%`n%y%`n%z%
To get with this modulo problem. replace the built-in function mod() by adding this function to your script, this will override the default mod() function:

Code: Select all

mod(a,b,decimalplaces:="") ; since autohotkey built-in modulo has a bug, this will fix the problem for integers and floating points
/*
computation was
if decimalplaces=
	return round(a - (b * floor(round(a/b,14))),14)+0 ; since 15th decimaplaces and above provides unexpected numbers, this fixes the problem
else return round(a - (b * floor(round(a/b,decimalplaces))),decimalplaces)+0 ; user can specify the decimal places of the rounding
modulo:=rtrim(rtrim(modulo,0),".") ; remove trailing zeros and dot if not needed
*/
{
  modulo:=((decimalplaces="")?(round(a - (b * floor(round(a/b,14))),14)):(round(a - (b * floor(round(a/b,decimalplaces))),decimalplaces)))+0
  return rtrim(rtrim(modulo,0),".")
}
floor() and ceil() is also most vulnerable to this issue since rounding up/down WHOLE NUMBERS in floating point format produces jagged numbers at its 15th to 32th decimal places.
eg. 0.99+0 results 0.98999999999999999111821580299875 using a 32 floating point precision.