Mod function returning wrong value?

Report problems with documented functionality
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Mod function returning wrong value?

28 Mar 2017, 06:39

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
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Mod function returning wrong value?

28 Mar 2017, 07:02

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
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Mod function returning wrong value?

28 Mar 2017, 07:48

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!
Last edited by jeeswg on 09 Apr 2017, 17:59, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Mod function returning wrong value?

28 Mar 2017, 08:36

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")
}
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Mod function returning wrong value?

28 Mar 2017, 10:17

Very good work jNizM, I'm most grateful.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Mod function returning wrong value?

09 Apr 2017, 18:08

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
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
aldrinjohnom
Posts: 77
Joined: 18 Apr 2018, 08:49

Re: Mod function returning wrong value?

27 Oct 2019, 21:54

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.
Developer of AJOM's DOTA2 MOD Master, Easiest way to MOD your DOTA2 without the use of internet :lol: . I created this program using Autohotkey thats why I love Autohotkey and the community!

GitHub:
https://github.com/Aldrin-John-Olaer-Manalansan

Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 28 guests