Thread overview
std.math.round() - doc bug?
Sep 28, 2005
Stefan Zobel
Sep 29, 2005
Don Clugston
Sep 29, 2005
Stefan Zobel
Sep 29, 2005
Don Clugston
Sep 29, 2005
Stefan Zobel
September 28, 2005
DMD 0.133 (Win XP)

The documentation for "real round(real x)" says:

# Return the value of x rounded to the nearest integer.
# If the fractional part of x is exactly 0.5,
# the return value is rounded to the even integer.

In reality, it seems to round the "halfway cases" (fractional part = 0.5)
away from zero. Considering that round() delegates to std.c.math.roundl()
that's not surprising (AFAIK, roundl() is defined to round "away from zero").

It appears that I have to use "real nearbyint(real x)" to achieve the desired "round to the even integer" behavior?

I'm by no means an expert in this matter. Can anyone confirm that this is a doc bug?

Best regards,
Stefan


September 29, 2005
Stefan Zobel wrote:
> DMD 0.133 (Win XP)
> 
> The documentation for "real round(real x)" says:
> 
> # Return the value of x rounded to the nearest integer.
> # If the fractional part of x is exactly 0.5,
> # the return value is rounded to the even integer.
> 
> In reality, it seems to round the "halfway cases" (fractional part = 0.5)
> away from zero. Considering that round() delegates to std.c.math.roundl()
> that's not surprising (AFAIK, roundl() is defined to round "away from zero").
> 
> It appears that I have to use "real nearbyint(real x)" to achieve the
> desired "round to the even integer" behavior?
> 
> I'm by no means an expert in this matter. Can anyone confirm that this
> is a doc bug?
> 
> Best regards,
> Stefan


It's definitely a doc bug.
Here's the behaviour of the existing functions:

round() --- rounds away from zero
trunc() --- rounds towards zero
floor() --- rounds towards negative infinity
ceil()  --- rounds towards positive infinity

nearbyint() --- round to even (use current rounding mode)
rint()      --- round to even (use current rounding mode)

This exactly matches the C99 behaviour. But it seems that there
is no way to ensure round-to-even if the current rounding mode
is changed.  I think that's a deficiency in C that D should rectify.

-Don
September 29, 2005
In article <dhg7cp$1m8d$1@digitaldaemon.com>, Don Clugston says...
>
>Stefan Zobel wrote:
>> DMD 0.133 (Win XP)
>> 
>> The documentation for "real round(real x)" says:
>> 
>> # Return the value of x rounded to the nearest integer.
>> # If the fractional part of x is exactly 0.5,
>> # the return value is rounded to the even integer.
>> 
>> In reality, it seems to round the "halfway cases" (fractional part = 0.5)
>> away from zero. Considering that round() delegates to std.c.math.roundl()
>> that's not surprising (AFAIK, roundl() is defined to round "away from zero").
>> 
>> It appears that I have to use "real nearbyint(real x)" to achieve the desired "round to the even integer" behavior?
>> 
>> I'm by no means an expert in this matter. Can anyone confirm that this is a doc bug?
>> 
>> Best regards,
>> Stefan
>
>
>It's definitely a doc bug.
>Here's the behaviour of the existing functions:
>
>round() --- rounds away from zero
>trunc() --- rounds towards zero
>floor() --- rounds towards negative infinity
>ceil()  --- rounds towards positive infinity
>
>nearbyint() --- round to even (use current rounding mode)
>rint()      --- round to even (use current rounding mode)
>
>This exactly matches the C99 behaviour. But it seems that there
>is no way to ensure round-to-even if the current rounding mode
>is changed.  I think that's a deficiency in C that D should rectify.
>
>-Don

Thanks for the clarification Don.
BTW, how can the current rounding mode be changed?
Other rounding modes are "towards negative infinity" and "towards positive
infinity" (converting rint() to floor() / ceil()), right?

Thanks,
Stefan


September 29, 2005
Stefan Zobel wrote:
> In article <dhg7cp$1m8d$1@digitaldaemon.com>, Don Clugston says...
> 
>>Stefan Zobel wrote:
>>
>>>DMD 0.133 (Win XP)
>>>
>>>The documentation for "real round(real x)" says:
>>>
>>># Return the value of x rounded to the nearest integer.
>>># If the fractional part of x is exactly 0.5,
>>># the return value is rounded to the even integer.
>>>
>>>In reality, it seems to round the "halfway cases" (fractional part = 0.5)
>>>away from zero. Considering that round() delegates to std.c.math.roundl()
>>>that's not surprising (AFAIK, roundl() is defined to round "away from zero").
>>>
>>>It appears that I have to use "real nearbyint(real x)" to achieve the
>>>desired "round to the even integer" behavior?
>>>
>>>I'm by no means an expert in this matter. Can anyone confirm that this
>>>is a doc bug?
>>>
>>>Best regards,
>>>Stefan
>>
>>
>>It's definitely a doc bug.
>>Here's the behaviour of the existing functions:
>>
>>round() --- rounds away from zero
>>trunc() --- rounds towards zero
>>floor() --- rounds towards negative infinity
>>ceil()  --- rounds towards positive infinity
>>
>>nearbyint() --- round to even (use current rounding mode)
>>rint()      --- round to even (use current rounding mode)
>>
>>This exactly matches the C99 behaviour. But it seems that there
>>is no way to ensure round-to-even if the current rounding mode
>>is changed.  I think that's a deficiency in C that D should rectify.
>>
>>-Don
> 
> 
> Thanks for the clarification Don.
> BTW, how can the current rounding mode be changed?
> Other rounding modes are "towards negative infinity" and "towards positive
> infinity" (converting rint() to floor() / ceil()), right?
> 
> Thanks,
> Stefan

There are four rounding modes in IEEE hardware -- nearest, floor, ceil, and trunc().
The round() function in C99 ("high school rounding") is not supported by the hardware, so it's significantly slower. I think it's ridiculous that the most accurate mode is not supported by C99.

I also note that in D, cast(int)(x) uses trunc(), following the (stupid and slow) C behaviour.

float.html describes how to change the rounding mode. It currently says "[blah, blah, blah]". In a post in dm.D yesterday, I asked Walter what his plans are. He doesn't yet have any.

In C, depending on the compiler, it's often done in a user-hostile way with _control87(). Less readable and less portable than asm.
C99 has fegetround() and fesetround() in fenv.h.

A more D-like alternative might be with properties; something like:

if (real.rounding != roundfloor) {
   typeof(real.rounding) oldmode = real.rounding;
   real.rounding = roundeven;

   real.rounding = oldmode;
}

but at present, all properties for built-in types are read-only.
I don't know if writable types would be a problem.
Rationale: the rounding mode behaves as if it is a static variable
which applies to every floating-point type. Ditto for precision.
Downside: it applies to every floating point type, including cfloat and idouble, which in D are not derived from real.

I haven't worked out how exceptions could work with properties, though.

Do you have any opinions/ideas on any of this?

-Don.
September 29, 2005
In article <dhgqgm$2ajg$1@digitaldaemon.com>, Don Clugston says...
>
>Stefan Zobel wrote:
>> In article <dhg7cp$1m8d$1@digitaldaemon.com>, Don Clugston says...
>>>
>>>It's definitely a doc bug.
>>>Here's the behaviour of the existing functions:
>>>
>>>round() --- rounds away from zero
>>>trunc() --- rounds towards zero
>>>floor() --- rounds towards negative infinity
>>>ceil()  --- rounds towards positive infinity
>>>
>>>nearbyint() --- round to even (use current rounding mode)
>>>rint()      --- round to even (use current rounding mode)
>>>
>>>This exactly matches the C99 behaviour. But it seems that there
>>>is no way to ensure round-to-even if the current rounding mode
>>>is changed.  I think that's a deficiency in C that D should rectify.
>>>
>>>-Don
>> 
>> 
>> Thanks for the clarification Don.
>> BTW, how can the current rounding mode be changed?
>> Other rounding modes are "towards negative infinity" and "towards positive
>> infinity" (converting rint() to floor() / ceil()), right?
>> 
>> Thanks,
>> Stefan
>
>There are four rounding modes in IEEE hardware -- nearest, floor, ceil,
>and trunc().
>The round() function in C99 ("high school rounding") is not supported by
>the hardware, so it's significantly slower. I think it's ridiculous that
>the most accurate mode is not supported by C99.

Don, thank you for the exemplification. However, I don't understand what your "most accurate" mode is (nearest?). And slightly OT: Why wouldn't it be supported by C99? I'd assume that nearbyint/rint take advantage of CPU instructions, or are they missing from the C99 standard?

>
>I also note that in D, cast(int)(x) uses trunc(), following the (stupid and slow) C behaviour.
>
>float.html describes how to change the rounding mode. It currently says "[blah, blah, blah]". In a post in dm.D yesterday, I asked Walter what his plans are. He doesn't yet have any.

Ah, I see.

>
>In C, depending on the compiler, it's often done in a user-hostile way
>with _control87(). Less readable and less portable than asm.
>C99 has fegetround() and fesetround() in fenv.h.
>
>A more D-like alternative might be with properties; something like:
>
>if (real.rounding != roundfloor) {
>    typeof(real.rounding) oldmode = real.rounding;
>    real.rounding = roundeven;
>
>    real.rounding = oldmode;
>}
>
>but at present, all properties for built-in types are read-only.
>I don't know if writable types would be a problem.
>Rationale: the rounding mode behaves as if it is a static variable
>which applies to every floating-point type. Ditto for precision.
>Downside: it applies to every floating point type, including cfloat and
>idouble, which in D are not derived from real.

Personally, I'd prefer a library call for that very reason, but that's just me.

Best regards,
Stefan

>
>I haven't worked out how exceptions could work with properties, though.
>
>Do you have any opinions/ideas on any of this?
>
>-Don.