Fwd: [Pharo-users] Set Rounding mode for IEEE floating point operations

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Fwd: [Pharo-users] Set Rounding mode for IEEE floating point operations

Steffen Märcker
 
Dear VM experts,

I originally asked the question below on pharo-users. Basically I am  
looking for a way to set the IEEE founding mode for fp-operations at  
runtime from the image.

Best, Steffen


Original mail to pharo-users:

Hi,

is there any way to set the rounding mode for IEEE floating point
operations? Maybe something like

> Double roundToMinusInfWhile: [... code goes here ...]
> Double roundToZeroWhile: [... more code here ...]

If not, is it possible to add this behavior, e.g., via a custom primitive?

Best, Steffen
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: [Pharo-users] Set Rounding mode for IEEE floating point operations

Levente Uzonyi
 
Hi Steffen,

As always, there are two ways to do this: via FFI or by writing a plugin
(which would implement the primitive).

For this case, FFI is probably easier to use. On POSIX platforms, you can
call the fegetround/fesetround C99 functions to get/set the rounding mode.
I have got no idea how to use FFI in Pharo, but in Squeak it would be as
simple as writing these two methods:

currentRoundingMode

  <cdecl: long 'fegetround' (void) module: 'libm'>
  ^self externalCallFailed

setRoundingModeTo: newMode
  "Set the rounding mode to newMode. Return zero on success, nonzero
on failure/unsupported mode.
  Modes:
  0      Rounding is toward 0.
  1      Rounding is toward nearest number.
  2      Rounding is toward positive infinity.
  3      Rounding is toward negative infinity.
  "

  <cdecl: long 'fesetround' (long) module: 'libm'>
  ^self externalCallFailed

Note that your platform may not support some rounding modes, and changing
the rounding mode may affect other parts of the VM.

Here's a quote from the GNU documentation about rounding modes:
"You should avoid changing the rounding mode if possible. It can be an
expensive operation; also, some hardware requires you to compile your
program differently for it to work. The resulting code may run slower. See
your compiler documentation for details."
So, you might have to tweak compiler flags and compile your own VM if
things don't work out of the box.

You might also find this answer helpful:
https://stackoverflow.com/questions/6867693/change-floating-point-rounding-mode#6867722

Levente

On Wed, 23 May 2018, Steffen Märcker wrote:

>
> Dear VM experts,
>
> I originally asked the question below on pharo-users. Basically I am
> looking for a way to set the IEEE founding mode for fp-operations at
> runtime from the image.
>
> Best, Steffen
>
>
> Original mail to pharo-users:
>
> Hi,
>
> is there any way to set the rounding mode for IEEE floating point
> operations? Maybe something like
>
>> Double roundToMinusInfWhile: [... code goes here ...]
>> Double roundToZeroWhile: [... more code here ...]
>
> If not, is it possible to add this behavior, e.g., via a custom primitive?
>
> Best, Steffen
>
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: [Pharo-users] Set Rounding mode for IEEE floating point operations

Nicolas Cellier
 
Interesting!
Are you after Interval arithmetic or something more complex?

Don't forget that the activeProcess can be preempted
- if a higher priority Process gets ready
- if it waits for a Semaphore
- if it explicitely yields and a process with same priority is ready
So Double roundToMinusInfWhile: [... code goes here ...] could have side effects on concurrent Process...
Unless the rounding mode is made a Process specific variable and that the ProcessorScheduler cares to restore appropriate rounding mode on Process switch if they differ... If the switch is too expensive, it could be differed until a floating point primitive is invoked (unfortunately FFI and plugins would have to be protected too).

Note that I have played with computation of residual errors on main operations (+ - * / sqrt) with fma or Kahan-sum-like technics.
This code could also be used for fully emulating some rounding mode at image side.
If it's only for Interval arithmetic, an image side solution could scale (more expensive than native hardware, but it's to be tested on macro-benchmark).
Tell me if your are interested.

Also note that unless you use some correctly rounded libm for the sin/cos/exp/ln... primitives (CRLIBM for example), then there is no much guaranty on the behaviour of these functions. The algorithm for Float>>raisedTo: and even raisedToInteger: is quite naive too, and will cumulate several ulp errors that a good libm/pow would not.

Please keep us informed on your progress :)

Nicolas

2018-05-23 19:23 GMT+02:00 Levente Uzonyi <[hidden email]>:
 
Hi Steffen,

As always, there are two ways to do this: via FFI or by writing a plugin (which would implement the primitive).

For this case, FFI is probably easier to use. On POSIX platforms, you can call the fegetround/fesetround C99 functions to get/set the rounding mode.
I have got no idea how to use FFI in Pharo, but in Squeak it would be as simple as writing these two methods:

currentRoundingMode

        <cdecl: long 'fegetround' (void) module: 'libm'>
        ^self externalCallFailed

setRoundingModeTo: newMode
        "Set the rounding mode to newMode. Return zero on success, nonzero on failure/unsupported mode.
        Modes:
                0      Rounding is toward 0.
                1      Rounding is toward nearest number.
                2      Rounding is toward positive infinity.
                3      Rounding is toward negative infinity.
        "

        <cdecl: long 'fesetround' (long) module: 'libm'>
        ^self externalCallFailed

Note that your platform may not support some rounding modes, and changing the rounding mode may affect other parts of the VM.

Here's a quote from the GNU documentation about rounding modes:
"You should avoid changing the rounding mode if possible. It can be an expensive operation; also, some hardware requires you to compile your program differently for it to work. The resulting code may run slower. See your compiler documentation for details."
So, you might have to tweak compiler flags and compile your own VM if things don't work out of the box.

You might also find this answer helpful: https://stackoverflow.com/questions/6867693/change-floating-point-rounding-mode#6867722

Levente

On Wed, 23 May 2018, Steffen Märcker wrote:


Dear VM experts,

I originally asked the question below on pharo-users. Basically I am looking for a way to set the IEEE founding mode for fp-operations at runtime from the image.

Best, Steffen


Original mail to pharo-users:

Hi,

is there any way to set the rounding mode for IEEE floating point
operations? Maybe something like

Double roundToMinusInfWhile: [... code goes here ...]
Double roundToZeroWhile: [... more code here ...]

If not, is it possible to add this behavior, e.g., via a custom primitive?

Best, Steffen


Reply | Threaded
Open this post in threaded view
|

Re: Fwd: [Pharo-users] Set Rounding mode for IEEE floating point operations

Steffen Märcker
In reply to this post by Levente Uzonyi
 
Hi Levente,

I managed to change the rounding mode via an FFI call (Pharo, OSX).  
However, the change seem to be not persistent/reliable, as the issues  
below suggest. I suspect some context switching/restauration to be the  
cause. Do you maybe have an idea what's going on?

Best, Steffen


Am .05.2018, 13:26 Uhr, schrieb Steffen Märcker <[hidden email]>:

> Hi,
>
> now I've observed the same issue. It might be related to context  
> switching, since introducing a delay has a similar effect. Consider:
>
>    | FE_TONEAREST FE_DOWNWARD FE_UPWARD FE_TOWARDZERO |
>    FE_TONEAREST  := 16r0000.
>    FE_DOWNWARD   := 16r0400.
>    FE_UPWARD     := 16r0800.
>    FE_TOWARDZERO := 16r0C00.
>    "For some reasons we have to call fegetround once."
>    "c := LibC new fegetround."
>    LibC new fesetround: FE_DOWNWARD.
>    (Delay forSeconds: 1) wait.
>    v1 := 1.0/10.0.
>    LibC new fesetround: FE_UPWARD.
>    v2 := 1.0/10.0.
>    LibC new fesetround: FE_TONEAREST.
>    v1 < v2.
>
> If the delay is inserted, the script evaluates to false. Using the same  
> LibC-instance or creating a new one does not seem to change anything  
> here. Interestingly, a similar approach in VisualWorks does not show  
> this issue yet.
>
> Actually, I expect the FE_* macros to be platform/implementation  
> dependent, as suggested here:
> http://www.enseignement.polytechnique.fr/informatique/INF478/docs/Cpp/en/c/numeric/fenv/FE_round.html
>
> [...]
>
> Am .05.2018, 11:57 Uhr, schrieb Serge Stinckwich  
> <[hidden email]>:
>
>> What is really strange is when I print the following lines, I obtain  
>> 2048:
>>
>> current := LibC uniqueInstance fegetround.
>> LibC uniqueInstance fesetround: 2048.
>> LibC uniqueInstance fegetround.
>>
>> and 0 when I remove the first line :
>> LibC uniqueInstance fesetround: 2048.
>> LibC uniqueInstance fegetround.
>>
>> [...]
>>
>>> On Thu, May 24, 2018 at 10:31 AM Steffen Märcker <[hidden email]>  
>>> wrote:
>>>
>>>> I actually made progress: It works like a charm! Basically, I
>>>> implemented
>>>> the same code as you. Testing is straightforward (using the constant
>>>> values from libc):
>>>>
>>>>    current := LibC fegetround.
>>>>    LibC fesetround: FE_DOWNWARDS.
>>>>    v1 := 1.0/10.0.
>>>>    LibC feesetround: FE_UPWARDS.
>>>>    v2 := 1.0/10.0.
>>>>    LibC feesetround: current.
>>>>    v1 < v2. "true"
>>>>
>>>> > ​but apparently nothing happens when you do :
>>>> >   LibC uniqueInstance fesetround: 1024.
>>>> >   LibC uniqueInstance fegetround.
>>>> > always return 0.​
>>>>
>>>> This is expected, since the fesetround function returns 0 only if the
>>>> set
>>>> operation was successful.
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: [Pharo-users] Set Rounding mode for IEEE floating point operations

Levente Uzonyi
 
Hi Steffen,

I can only guess that there's some library (IIRC Pharo uses e.g. FreeType)
used by the VM setting the rounding mode for its own purposes.
I would try to see if the rounding mode stays the same while vmParameter
56 (number of process switches since startup) stays the same. And if it
does, I'd use that to minimize the number of times the FFI call has to be
done to have the rounding mode you need. Because polling the parameter is
probably faster than the FFI call.

Levente

On Thu, 24 May 2018, Steffen Märcker wrote:

>
> Hi Levente,
>
> I managed to change the rounding mode via an FFI call (Pharo, OSX).
> However, the change seem to be not persistent/reliable, as the issues
> below suggest. I suspect some context switching/restauration to be the
> cause. Do you maybe have an idea what's going on?
>
> Best, Steffen
>
>
> Am .05.2018, 13:26 Uhr, schrieb Steffen Märcker <[hidden email]>:
>
>> Hi,
>>
>> now I've observed the same issue. It might be related to context
>> switching, since introducing a delay has a similar effect. Consider:
>>
>>    | FE_TONEAREST FE_DOWNWARD FE_UPWARD FE_TOWARDZERO |
>>    FE_TONEAREST  := 16r0000.
>>    FE_DOWNWARD   := 16r0400.
>>    FE_UPWARD     := 16r0800.
>>    FE_TOWARDZERO := 16r0C00.
>>    "For some reasons we have to call fegetround once."
>>    "c := LibC new fegetround."
>>    LibC new fesetround: FE_DOWNWARD.
>>    (Delay forSeconds: 1) wait.
>>    v1 := 1.0/10.0.
>>    LibC new fesetround: FE_UPWARD.
>>    v2 := 1.0/10.0.
>>    LibC new fesetround: FE_TONEAREST.
>>    v1 < v2.
>>
>> If the delay is inserted, the script evaluates to false. Using the same
>> LibC-instance or creating a new one does not seem to change anything
>> here. Interestingly, a similar approach in VisualWorks does not show
>> this issue yet.
>>
>> Actually, I expect the FE_* macros to be platform/implementation
>> dependent, as suggested here:
>>
> http://www.enseignement.polytechnique.fr/informatique/INF478/docs/Cpp/en/c/numeric/fenv/FE_round.html
>>
>> [...]
>>
>> Am .05.2018, 11:57 Uhr, schrieb Serge Stinckwich
>> <[hidden email]>:
>>
>>> What is really strange is when I print the following lines, I obtain
>>> 2048:
>>>
>>> current := LibC uniqueInstance fegetround.
>>> LibC uniqueInstance fesetround: 2048.
>>> LibC uniqueInstance fegetround.
>>>
>>> and 0 when I remove the first line :
>>> LibC uniqueInstance fesetround: 2048.
>>> LibC uniqueInstance fegetround.
>>>
>>> [...]
>>>
>>>> On Thu, May 24, 2018 at 10:31 AM Steffen Märcker <[hidden email]>
>>>> wrote:
>>>>
>>>>> I actually made progress: It works like a charm! Basically, I
>>>>> implemented
>>>>> the same code as you. Testing is straightforward (using the constant
>>>>> values from libc):
>>>>>
>>>>>    current := LibC fegetround.
>>>>>    LibC fesetround: FE_DOWNWARDS.
>>>>>    v1 := 1.0/10.0.
>>>>>    LibC feesetround: FE_UPWARDS.
>>>>>    v2 := 1.0/10.0.
>>>>>    LibC feesetround: current.
>>>>>    v1 < v2. "true"
>>>>>
>>>>> > ​but apparently nothing happens when you do :
>>>>> >   LibC uniqueInstance fesetround: 1024.
>>>>> >   LibC uniqueInstance fegetround.
>>>>> > always return 0.​
>>>>>
>>>>> This is expected, since the fesetround function returns 0 only if the
>>>>> set
>>>>> operation was successful.
>
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: [Pharo-users] Set Rounding mode for IEEE floating point operations

Steffen Märcker
 
Hi Levente,

I'll have a look, thanks for your suggestion! From your description, I  
assume, libraries as Freetype run in the same process, right? If so, a  
library changing those process-flags, might cause (maybe hidden) issues at  
other places as well.

Best, Steffen



Am .05.2018, 00:43 Uhr, schrieb Levente Uzonyi <[hidden email]>:

> Hi Steffen,
> I can only guess that there's some library (IIRC Pharo uses e.g.  
> FreeType)
> used by the VM setting the rounding mode for its own purposes.
> I would try to see if the rounding mode stays the same while vmParameter
> 56 (number of process switches since startup) stays the same. And if it  
> does, I'd use that to minimize the number of times the FFI call has to  
> be done to have the rounding mode you need. Because polling the  
> parameter is probably faster than the FFI call.
> Levente