Jump to content


[AHK_L] Round(-0.001, 2) returns -0.00


  • Please log in to reply
17 replies to this topic

#1 Guests

  • Guests

Posted 13 April 2011 - 06:31 AM

[Moderator's note: Topic split from AutoHotkey v2 Alpha Release.]

Round(-0.001, 2) returns -0.00. How can i get 0.00?

#2 Guests

  • Guests

Posted 14 April 2011 - 05:05 PM

Round(-0.001, 2) returns -0.00. How can i get 0.00?


SubStr(Round(-0.001, 2) + 0, 1, -4)

Is there any better way? In my opinion, Round(-0.001, 2) should return 0.00 as in original AutoHotkey.

#3 infogulch

infogulch
  • Moderators
  • 717 posts

Posted 14 April 2011 - 05:09 PM

Round(-0.001, 2) returns -0.00. How can i get 0.00?

SubStr(Round(-0.001, 2) + 0, 1, -4)

Whoa whoa whoa -- use Abs()

#4 Guests

  • Guests

Posted 14 April 2011 - 05:51 PM

Round(-0.001, 2) returns -0.00. How can i get 0.00?

SubStr(Round(-0.001, 2) + 0, 1, -4)

Whoa whoa whoa -- use Abs()


I am rounding numbers to 2 decimal places. I want 0.00, not 0 or 0.000000.

#5 infogulch

infogulch
  • Moderators
  • 717 posts

Posted 14 April 2011 - 05:55 PM

I am rounding numbers to 2 decimal places. I want 0.00, not 0 or 0.000000.

Then use Abs before you use round.
Round(Abs(-0.001), 2)

MOD: This has gone a bit too far off topic imo, split to help?

#6 Guests

  • Guests

Posted 14 April 2011 - 06:08 PM

I am rounding numbers to 2 decimal places. I want 0.00, not 0 or 0.000000.

Then use Abs before you use round.
Round(Abs(-0.001), 2)


What's Abs() got to do with rounding numbers to 2 decimal places? Round(Abs(-0.01), 2) returns 0.01 not -0.01.

And is there some reason to this negative zero? Round(-0.001, 2) returns 0.00 in original AutoHotkey and AutoHotkey_L has SetFormat.

#7 Lexikos

Lexikos
  • Administrators
  • 8855 posts

Posted 14 April 2011 - 10:20 PM

Either of the following should do the job:
MsgBox % Round(Round(-0.001, 2), 2)
MsgBox % (n := Round(-0.001, 2)) = 0 ? "0.00" : n
The difference in behaviour is due to the use of ceil() from the C runtime instead of qmathCeil(), which iirc isn't 64-bit compatible. The following (C++) code returns -0.000000 when value is -0.001:
aResultToken.value_double = (value >= 0.0 ? qmathFloor(value * multiplier + 0.5)
	: qmathCeil(value * multiplier - 0.5)) / multiplier;
(qmathCeil and qmathFloor are now just macros which point at ceil and floor.)

Round(..., 2) uses _stprintf to format the number. Apparently the -0.00 result is intentional:

Note that the change is not a change to the double representation, but a change to printf (and other floating point output functions). They previously ignored negative zeros, but now they do not. This brings us into line with the relevant standards, in response to customer requests.
Source: Negative zero valid?



#8 Guests

  • Guests

Posted 15 April 2011 - 07:52 AM

Thanks for clarification. I didn't realise that Round(-0.00, 2) returns 0.00, not -0.00. By the way, how does that comform to the IEEE standard?

If negative zero is the standard, would it be logical then to expect that also Round(-0.1) returns -0. Maybe this could be added to the documentation.


v1.0.44.01+, a value of N greater than zero displays exactly N decimal places rather than obeying SetFormat. To avoid this, perform another math operation on Round()'s return value; for example: Round(3.333, 1)+0.


Is it intentional that comparison operations like equal cause this same effect. This is not good imo and does not happen in original AHK or v2.

#9 Lexikos

Lexikos
  • Administrators
  • 8855 posts

Posted 15 April 2011 - 09:21 AM

By the way, how does that comform to the IEEE standard?

I'm not familiar with the IEEE standard. Feel free to look it up if you're curious. (Edit: I was curious. I was unable to find a copy of the IEEE standard, but I did find something suggesting the return value of standard ceil() always has the same sign as its input.)

If negative zero is the standard, would it be logical then to expect that also Round(-0.1) returns -0.

When N is 0 or omitted, Round returns a pure integer, not a formatted floating-point numeric string. Integers have no way to represent negative zero.

Note that the expression (0.0 * -1.0) produces negative zero.

Is it intentional that comparison operations like equal cause this same effect.

What are you talking about?

#10 jaco0646

jaco0646
  • Fellows
  • 3163 posts

Posted 15 April 2011 - 02:51 PM

[OffTopic]
The talk of negative zero reminded me that SetTimer treats 0 and -0 as equivalent, when the negative sign is supposed to indicate Run only once.
[/OffTopic]

#11 Guests

  • Guests

Posted 15 April 2011 - 04:42 PM

What are you talking about?


Comparison operation with -0.00 sets format to 0.000000.

#12 Lexikos

Lexikos
  • Administrators
  • 8855 posts

Posted 16 April 2011 - 12:14 AM

Please provide an example.

x := Round(-0.001, 2)
MsgBox % x ; -0.00
if (x = 0)
    MsgBox zero
MsgBox % x ; -0.00


#13 Guests

  • Guests

Posted 16 April 2011 - 04:31 AM

Sorry, I did remember wrong. It was -0.000000 and when using a function. Without comparison the function gives -0.00.

MsgBox, % func()   ; AHK_L: -0.000000 / AHK v2: -0.00 / AHK: 0.00

func()
{
    x := Round(-0.001, 2)
    if (x = 0) {
    }
    return x
}


#14 Solar

Solar
  • Members
  • 345 posts

Posted 16 April 2011 - 06:17 AM

Sorry, I did remember wrong. It was -0.000000 and when using a function. Without comparison the function gives -0.00.

To be clear, this has nothing to do with -0.0 or the round function. The following example will give the same effect:
MsgBox % func()

func() {
    x := 1.0
    if (x) {
    }
    return x
}
Why? Well, based on the next example of a workaround, it seems to be a bug in the way return handles expressions:
MsgBox % func()

func() {
    x := 1.0
    if (x) {
    }
    return [color=red]([/color]x[color=red])[/color]
}


#15 Lexikos

Lexikos
  • Administrators
  • 8855 posts

Posted 16 April 2011 - 01:30 PM

I see.

Well, based on the next example of a workaround, it seems to be a bug in the way return handles expressions

Return handles expressions just fine, but return x and return %x% (which are equivalent in v1) are not expressions.

Note that y := (x + 0) will have the same effect as if (x = 0): when the variable x is converted to a number, the result is stored in x's binary number cache. This "read-caching" is done in AutoHotkey Basic and AutoHotkey_L v1, but not v2. Similarly, if x contains only a pure number, an expression like (x . "") causes a string to be stored in x alongside the number. This is done in AutoHotkey Basic, AutoHotkey_L v1 and v2. So in v1, it's impossible to distinguish between x, y and z at the end of the following script:
;     NUM  STR  V1 | V2  NUM  STR
y := "1.000000" ; y    0    1      |      0    1
z := 1.000000+0 ; z    1    0      |      1    0
x := 1.000000   ; x    1    1      |      1    0
_ := y [color=red]+ 0[/color]      ; y    [color=red]1[/color]    1      |      0    1
_ := z [color=red]. ""[/color]     ; z    1    [color=red]1[/color]      |      1    [color=red]1[/color]
In AutoHotkey Basic, all return values from user-defined functions are strings, so your numeric string retains its format.

In AutoHotkey_L v1, the expression evaluator returns all variables as strings, so return (x) works in this case. However, if the return value (which is a string) is passed to something which requires a number (such as a particular COM object method), it will fail. On the other hand, return x bypasses the expression evaluator and uses the attributes of the variable to determine which type of value to return. If it contains a number, only the number is returned. This enables certain COM object methods to work correctly, but causes any special formatting of the numeric string to be lost. The behaviour of return (x) is correct from a backward-compatibility standpoint, but was actually unintentional. Conversely, the behaviour of return x was (perhaps misguidedly) intentional, but it breaks compatibility.

In v2, there is no number "cache". A variable can only have the "numeric" flag if it was originally assigned a number. If x contains a number, both return (x) and return x return only a pure number. Since Round(...,2) returns a string, x contains a string; both return (x) and return x return the original string. A numeric variable can also have a string component, but since SetFormat has been eliminated, the string component can be discarded and regenerated at any time.

Side note: passing the variable through SubStr prevents binary number caching from occurring.
func()
{
    x := Round(-0.001, 2)
    if (SubStr(x,1) = 0) {
    }
    return x ; -0.00
}
Additionally, if you use a quoted literal string, the comparison is always done alphabetically in v1 since v1 considers all quoted literal strings non-numeric. This prevents the expression from caching a number in x.
MsgBox, % Round2(-0.001)

Round2(x) {
    x := Round(x, 2)
    if (x = "-0.00")
        x := "0.00"
    return x
}