Rounding Bug

Report problems with documented functionality
User avatar
oldbrother
Posts: 265
Joined: 23 Oct 2013, 05:08

Rounding Bug

Post by oldbrother » 30 Oct 2022, 08:58

This is a bug to me:

Version 1.1.34.04
Version 1.1.35

Code: Select all

Result1 := round(1.035,2) ;Returns 1.03. It's not correct!!!
Result2 := round(1.045,2) ;Returns 1.05. Correct

Msgbox %Result1%       %Result2%
Return

User avatar
thqby
Posts: 391
Joined: 16 Apr 2021, 11:18
Contact:

Re: Rounding Bug

Post by thqby » 30 Oct 2022, 19:07

You think it is 1.035. Due to the precision error of 64 bit floating point number, the binary storage may be 1.03499999999998, so this is not a bug in the current implementation.

User avatar
oldbrother
Posts: 265
Joined: 23 Oct 2013, 05:08

Re: Rounding Bug

Post by oldbrother » 30 Oct 2022, 20:24

Thank you thqby. But round(1.035,2) returns 1.03 is still a mistake. Should I use a walk-around function like this:

Code: Select all

SRound(Num,P)
{
  Num := Num + 0.00000000001
  Return % Round(Num,P)
}

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Rounding Bug

Post by malcev » 31 Oct 2022, 08:36

Due to the precision error of 64 bit floating point number, the binary storage may be 1.034
Though it seems that it is bug with round: (or may be with format)

Code: Select all

msgbox % Round(1.005, 2) " " Format("{:.20f}", 1.005) "`n" Round(1.0005, 3) " " Format("{:.20f}", 1.0005)

joefiesta
Posts: 494
Joined: 24 Jan 2016, 13:54
Location: Pa., USA

Re: Rounding Bug

Post by joefiesta » 31 Oct 2022, 12:20

how does one actually display the true binary storage (hex) of 1.035?

lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Rounding Bug

Post by lexikos » 01 Nov 2022, 04:08

These types of issues have been reported and explained multiple times, and are a relatively common cause of confusion for programmers of whatever language. It should perhaps be documented - at least - that AutoHotkey uses whatever common binary floating-point format corresponds to double in MVSC++ on x86/x64 (although I think that most users will not know what to make of these details, or understand the implications of binary floating-point).

If you want to understand the issue, I suggest searching about binary floating-point formats, floating-point rounding errors, and binary vs. decimal floating-point; e.g. math - Why can't decimal numbers be represented exactly in binary? - Stack Overflow.

@oldbrother The mistake is assuming that 1.035 = 1035/1000, when a binary floating-point format is in use. A simplified explanation is that the decimal value 1.035 cannot be represented exactly, so the actual binary value is an approximation.

@malcev I don't see any reason to believe so.

@joefiesta True binary storage consists of a collection of units where each unit can be in exactly two possible states, hence binary. Hex is not true binary storage (or storage at all; it's a display format). It is more convenient than decimal for viewing binary data because hexadecimal is base 16, 16 is a power of 2, and binary is base 2. Each hexadecimal digit represents 4 bits.

You can put a float or double value with NumPut, retrieve it as an integer with NumGet and use bitwise operations to examine each bit, but interpreting the bits would require an understanding of the floating-point format.

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Rounding Bug

Post by malcev » 01 Nov 2022, 04:44

@lexikos, here I think round should return 1.000.
For example if we run through chakra javascript it returns ok.

Code: Select all

num := 1.0005
n := 3
js := new JsRT.Edge
MsgBox % "chakra - " js.Eval(num ".toFixed(" n ");") "`nround - " Round(num, n) "`nformat - " Format("{:.20f}", num)

/*
 *  ActiveScript for AutoHotkey v1.1
 *
 *  Provides an interface to Active Scripting languages like VBScript and JScript,
 *  without relying on Microsoft's ScriptControl, which is not available to 64-bit
 *  programs.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class ActiveScript extends ActiveScript._base
{
    __New(Language)
    {
        if this._script := ComObjCreate(Language, ActiveScript.IID)
            this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
        if !this._scriptParse
            throw Exception("Invalid language", -1, Language)
        this._site := new ActiveScriptSite(this)
        this._SetScriptSite(this._site.ptr)
        this._InitNew()
        this._objects := {}
        this.Error := ""
        this._dsp := this._GetScriptDispatch()  ; Must be done last.
        try
            if this.ScriptEngine() = "JScript"
                this.SetJScript58()
    }

    SetJScript58()
    {
        static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
        if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
            return false
        VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
        hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
            , "ptr", 0, "ptr", &var), ObjRelease(prop)
        return hr >= 0
    }
    
    Eval(Code)
    {
        pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
        this._ParseScriptText(Code, 0x20, pvar)  ; SCRIPTTEXT_ISEXPRESSION := 0x20
        return arr[0]
    }
    
    Exec(Code)
    {
        this._ParseScriptText(Code, 0x42, 0)  ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
        this._SetScriptState(2)  ; SCRIPTSTATE_CONNECTED := 2
    }
    
    AddObject(Name, DispObj, AddMembers := false)
    {
        static a, supports_dispatch ; Test for built-in IDispatch support.
            := a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
        if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
            throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
        this._objects[Name] := DispObj
        this._AddNamedItem(Name, AddMembers ? 8 : 2)  ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
    }
    
    _GetObjectUnk(Name)
    {
        return !IsObject(dsp := this._objects[Name]) ? dsp  ; Pointer
            : ComObjValue(dsp) ? ComObjValue(dsp)  ; ComObject
            : &dsp  ; AutoHotkey object
    }
    
    class _base
    {
        __Call(Method, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Method](Params*)
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Get(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Property, Params*]
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Set(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
            {
                Value := Params.Pop()
                try
                    return (this._dsp)[Property, Params*] := Value
                catch e
                    throw Exception(e.Message, -1, e.Extra)
            }
        }
    }
    
    _SetScriptSite(Site)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptSite")
    }
    
    _SetScriptState(State)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptState")
    }
    
    _AddNamedItem(Name, Flags)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::AddNamedItem")
    }
    
    _GetScriptDispatch()
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::GetScriptDispatch")
        return ComObject(9, pdsp, 1)
    }
    
    _InitNew()
    {
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::InitNew")
    }
    
    _ParseScriptText(Code, Flags, pvarResult)
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
            , "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
            , "uint", Flags, "ptr", pvarResult, "ptr", 0)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
    }
    
    _HRFail(hr, what)
    {
        if e := this.Error
        {
            this.Error := ""
            throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
                . "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
                . "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
                . "`nLine text:`t`t" e.LineText, -3)
        }
        throw Exception(what " failed with code " this._HRFormat(hr), -2)
    }
    
    _HRFormat(hr)
    {
        return Format("0x{1:X}", hr & 0xFFFFFFFF)
    }
    
    _OnScriptError(err) ; IActiveScriptError err
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
        DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
        DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
        code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
        if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
            DllCall(fn, "ptr", &excp)
        wcode := NumGet(excp, 0, "ushort")
        hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
        this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
        static Infos := "Source,Description,HelpFile"
        Loop Parse, % Infos, `,
            if pbstr := NumGet(excp, A_Index * A_PtrSize)
                this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
        return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
    }
    
    __Delete()
    {
        if this._script
        {
            DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p)  ; Close
            ObjRelease(this._script)
        }
        if this._scriptParse
            ObjRelease(this._scriptParse)
    }
    
    static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
    static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}

class ActiveScriptSite
{
    __New(Script)
    {
        ObjSetCapacity(this, "_site", 3 * A_PtrSize)
        NumPut(&Script
        , NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
        , NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
            , this.ptr := ObjGetAddress(this, "_site"))))
    }
    
    _vftable(Name, PrmCounts, EIBase)
    {
        if p := ObjGetAddress(this, Name)
            return p
        ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
        p := ObjGetAddress(this, Name)
        Loop Parse, % PrmCounts
        {
            cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
            NumPut(cb, p + (A_Index-1) * A_PtrSize)
        }
        return p
    }
}

_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
    Method := A_EventInfo & 0xFF
    if A_EventInfo >= 0x100  ; IActiveScriptSiteWindow
    {
        if Method = 4  ; GetWindow
        {
            NumPut(0, a1+0) ; *phwnd := 0
            return 0 ; S_OK
        }
        if Method = 5  ; EnableModeless
        {
            return 0 ; S_OK
        }
        this -= A_PtrSize     ; Cast to IActiveScriptSite
    }
    ;else: IActiveScriptSite
    if Method = 1  ; QueryInterface
    {
        iid := _AS_GUIDToString(a1)
        if (iid = "{00000000-0000-0000-C000-000000000046}"  ; IUnknown
         || iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
        {
            NumPut(this, a2+0)
            return 0 ; S_OK
        }
        if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
        {
            NumPut(this + A_PtrSize, a2+0)
            return 0 ; S_OK
        }
        NumPut(0, a2+0)
        return 0x80004002 ; E_NOINTERFACE
    }
    if Method = 5  ; GetItemInfo
    {
        a1 := StrGet(a1, "UTF-16")
        , (a3 && NumPut(0, a3+0))  ; *ppiunkItem := NULL
        , (a4 && NumPut(0, a4+0))  ; *ppti := NULL
        if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
        {
            if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
                return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
            ObjAddRef(unk), NumPut(unk, a3+0)
        }
        return 0 ; S_OK
    }
    if Method = 9  ; OnScriptError
        return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
    
    ; AddRef and Release don't do anything because we want to avoid circular references.
    ; The site and IActiveScript are both released when the AHK script releases its last
    ; reference to the ActiveScript object.
    
    ; All of the other methods don't require implementations.
    return 0x80004001 ; E_NOTIMPL
}

_AS_GUIDToString(pGUID)
{
    VarSetCapacity(String, 38*2)
    DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
    return String
}


/*
 *  JsRT for AutoHotkey v1.1
 *
 *  Utilizes the JavaScript engine that comes with IE11.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class JsRT extends ActiveScript._base
{
    __New()
    {
        throw Exception("This class is abstract. Use JsRT.IE or JSRT.Edge instead.", -1)
    }
    
    class IE extends JsRT
    {
        __New()
        {
            if !this._hmod := DllCall("LoadLibrary", "str", "jscript9", "ptr")
                throw Exception("Failed to load jscript9.dll", -1)
            if DllCall("jscript9\JsCreateRuntime", "int", 0, "int", -1
                , "ptr", 0, "ptr*", runtime) != 0
                throw Exception("Failed to initialize JsRT", -1)
            DllCall("jscript9\JsCreateContext", "ptr", runtime, "ptr", 0, "ptr*", context)
            this._Initialize("jscript9", runtime, context)
        }
    }
    
    class Edge extends JsRT
    {
        __New()
        {
            if !this._hmod := DllCall("LoadLibrary", "str", "chakra", "ptr")
                throw Exception("Failed to load chakra.dll", -1)
            if DllCall("chakra\JsCreateRuntime", "int", 0
                , "ptr", 0, "ptr*", runtime) != 0
                throw Exception("Failed to initialize JsRT", -1)
            DllCall("chakra\JsCreateContext", "ptr", runtime, "ptr*", context)
            this._Initialize("chakra", runtime, context)
        }
        
        ProjectWinRTNamespace(namespace)
        {
            return DllCall("chakra\JsProjectWinRTNamespace", "wstr", namespace)
        }
    }
    
    _Initialize(dll, runtime, context)
    {
        this._dll := dll
        this._runtime := runtime
        this._context := context
        DllCall(dll "\JsSetCurrentContext", "ptr", context)
        DllCall(dll "\JsGetGlobalObject", "ptr*", globalObject)
        this._dsp := this._JsToVt(globalObject)
    }
    
    __Delete()
    {
        this._dsp := ""
        if dll := this._dll
        {
            DllCall(dll "\JsSetCurrentContext", "ptr", 0)
            DllCall(dll "\JsDisposeRuntime", "ptr", this._runtime)
        }
        DllCall("FreeLibrary", "ptr", this._hmod)
    }
    
    _JsToVt(valref)
    {
        VarSetCapacity(variant, 24, 0)
        DllCall(this._dll "\JsValueToVariant", "ptr", valref, "ptr", &variant)
        ref := ComObject(0x400C, &variant), val := ref[], ref[] := 0
        return val
    }
    
    _ToJs(val)
    {
        VarSetCapacity(variant, 24, 0)
        ref := ComObject(0x400C, &variant) ; VT_BYREF|VT_VARIANT
        ref[] := val
        DllCall(this._dll "\JsVariantToValue", "ptr", &variant, "ptr*", valref)
        ref[] := 0
        return valref
    }
    
    _JsEval(code)
    {
        e := DllCall(this._dll "\JsRunScript", "wstr", code, "uptr", 0, "wstr", "source.js"
            , "ptr*", result)
        if e
        {
            if DllCall(this._dll "\JsGetAndClearException", "ptr*", excp) = 0
                throw this._JsToVt(excp)
            throw Exception("JsRT error", -2, format("0x{:X}", e))
        }
        return result
    }
    
    Exec(code)
    {
        this._JsEval(code)
    }
    
    Eval(code)
    {
        return this._JsToVt(this._JsEval(code))
    }
    
    AddObject(name, obj, addMembers := false)
    {
        if addMembers
            throw Exception("AddMembers=true is not supported", -1)
        this._dsp[name] := obj
    }
}
@oldbrother, as I see if we run foFixed method through ie javascript it rounds as You need:, because ie stores num as string.

Code: Select all

num := 1.0005
n := 3
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
msgbox % doc.parentWindow.eval(num ".toFixed(" n ");")
But if You want to work with rounding of any float number then You need to create rounded operation by Yourself - parse every character of input string and increase it by 1 if previous character is 9.

User avatar
oldbrother
Posts: 265
Joined: 23 Oct 2013, 05:08

Re: Rounding Bug

Post by oldbrother » 02 Nov 2022, 07:40

@lexikos, It will be a disaster if Round(1.035,2) in Excel returns 1.03. Why don't we make Round() function in AHK to return correct values?

Thank you all!

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Rounding Bug

Post by malcev » 02 Nov 2022, 08:36

For such wish need to change algorithm of all math functions with floats.
Example

Code: Select all

var1 := 1.035
var2 := 1.036 - 0.001
if (var1 = var2)
   msgbox equal
else
   msgbox no

loop 2
{
   oVar%A_Index% := StrSplit(var%A_Index%, ".")
   oVar%A_Index%[2] := RTrim(oVar%A_Index%[2], 0)
}
if (oVar1[1] "" = oVar2[1]) and (oVar1[2] "" = oVar2[2])
   msgbox equal
else
   msgbox no
Just use my example with js ie.

joefiesta
Posts: 494
Joined: 24 Jan 2016, 13:54
Location: Pa., USA

Re: Rounding Bug

Post by joefiesta » 02 Nov 2022, 11:44

the problem also does not occur using REXX.

lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Rounding Bug

Post by lexikos » 02 Nov 2022, 20:38

I don't care what other languages or programs do, but just for reference, round(1.035, 2) in Python returns 1.03, and round(1.036 - 0.001, 2) returns 1.04.

You're missing the point that 1.035 is not the number you think it is. The imprecision is not coming from Round. If you want to change Round to "fix" it, all you will be doing is making Round less precise.

viewtopic.php?t=2305
The default float formatting in v1 hides the issue from you. In v2, you see a value closer to the truth.

Code: Select all

MsgBox 1.035  ; 1.0349999999999999
MsgBox 1.036 - 0.001 ; 1.0350000000000001
lexikos wrote:
01 Nov 2022, 04:08
If you want to understand the issue, I suggest searching about binary floating-point formats, floating-point rounding errors, and binary vs. decimal floating-point; e.g. math - Why can't decimal numbers be represented exactly in binary? - Stack Overflow.

Changing the floating-point number format to one that doesn't have these issues (and isn't natively supported by the CPU) is well outside the scope of what can be done without breaking scripts. I am not interested in doing the research and testing required to not only implement such a change, but to evaluate whether it is even appropriate in the first place.

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Rounding Bug

Post by malcev » 02 Nov 2022, 21:18

@lexikos, if You think that there are no bugs with round in ahk, then can You explain why

Code: Select all

msgbox % Round(1.0005, 3)
returns 1.001?
By the way in python it returns correct - 1.0.

lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Rounding Bug

Post by lexikos » 03 Nov 2022, 19:02

Can you explain why you think that 1.001 is incorrect and 1.0 is correct?

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Rounding Bug

Post by malcev » 03 Nov 2022, 19:47

Because

Code: Select all

msgbox % 1.0005
returns 1.0004999999999999
And

Code: Select all

msgbox % Round(1.0004999999999999, 3)
should return 1.000 (because 4 is not 5).
For example

Code: Select all

msgbox % Round(2.0004999999999999, 3)
returns correct 2.000.

lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Rounding Bug

Post by lexikos » 03 Nov 2022, 20:04

Where in the documentation is the rounding behaviour specified?

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Rounding Bug

Post by malcev » 03 Nov 2022, 20:33

Nowhere. But dont You think that behaviour is not logical?

Code: Select all

msgbox % Round(1.00049999999999983, 3) "`n" Format("{:.20f}", 1.00049999999999983)   ; logical
msgbox % Round(1.00049999999999984, 3) "`n" Format("{:.20f}", 1.00049999999999984)   ; not logical

lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Rounding Bug

Post by lexikos » 03 Nov 2022, 20:44

No, I do not. Like I said multiple times, if you want to understand the issue, you can do your own research.

No matter what rounding method you use, you will get unexpected results unless you temper your expectations with an understanding of the limitations of the binary floating-point format.

For instance, how would you round 1.0005 to 3 decimal places, with only a function to round to 0 decimal places?

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Rounding Bug

Post by malcev » 03 Nov 2022, 21:11

Code: Select all

#include <iostream>
#include <cmath>
using namespace std;

int main() {
  double result;

  float num = 1.00049999999999984;
  result = round(num*1000)/1000;

  cout << "round(" << num << ") = " << result;

  return 0;
}
It shows correct value - 1.

lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Rounding Bug

Post by lexikos » 03 Nov 2022, 22:10

It shows a value of 1, which you claim is correct. The input you have given is not 1.0005, and you haven't specified the rules for rounding. The input is not even 1.00049999999999984 as written, because you are mixing 32-bit float with 64-bit double. If you correct this mistake, i.e. use double throughout, the result will be 1.001, because 1.00049999999999984 * 1000 = 1000.5.

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Rounding Bug

Post by malcev » 04 Nov 2022, 00:05

I corrected mistake, now it shows the result that I am expecting.

Code: Select all

#include <iostream>
#include <cmath>
using namespace std;

int main() {
  double result;

  double num = 1.0005;
  result = round(num/0.001)*0.001;

  cout << "round(" << num << ") = " << result;

  return 0;
}
Also found one more way to get topicstarter needed result using any js engine.

Code: Select all

num := 1.0005
n := 3
js := new JsRT.Edge
MsgBox % (num<0 ? "-" : "") js.Eval("Number(Math.round(" abs(num) "+'e" n "')+'e-" n "');")
Last edited by malcev on 05 Nov 2022, 09:03, edited 1 time in total.

Post Reply

Return to “Bug Reports”