[Vm-dev] Potential issue of primitiveTimesTwoPower in Spur 64
Eliot Miranda
eliot.miranda at gmail.com
Thu Feb 12 03:19:55 UTC 2015
On Wed, Feb 11, 2015 at 6:27 PM, Nicolas Cellier <
nicolas.cellier.aka.nice at gmail.com> wrote:
>
>
>
> 2015-02-12 3:02 GMT+01:00 Eliot Miranda <eliot.miranda at gmail.com>:
>
>>
>> Again not close enough. If value < 0, then result is < 0, so
>>
>> primitiveTimesTwoPower
>> <option: #Spur64BitMemoryManager>
>> | rcvr result arg twiceMaxExponent |
>> <var: #rcvr type: #double>
>> <var: #result type: #double>
>> arg := self stackTop.
>> (objectMemory isIntegerObject: arg) ifFalse:
>> [^self primitiveFailFor: PrimErrBadArgument].
>> rcvr := objectMemory floatValueOf: (self stackValue: 1).
>> ((self isFinite: rcvr) and: [rcvr ~= 0])
>> ifFalse:
>> [result := rcvr]
>> ifTrue:
>> [arg := objectMemory integerValueOf: arg.
>> twiceMaxExponent := 2 * (1 << self floatExponentBits).
>> arg < twiceMaxExponent negated
>> ifTrue:
>> [result := rcvr < 0.0 ifTrue: [-0.0] ifFalse: [0.0]]
>> ifFalse:
>> [arg > twiceMaxExponent
>> ifTrue:
>> [result := rcvr < 0.0 ifTrue: [-1.0e200 / 10.e-200] ifFalse: 1.0e200 /
>> 1.0e-200]]
>> ifFalse:
>> [result := self cCode: [self ld: rcvr exp: arg]
>> inSmalltalk: [rcvr timesTwoPower: arg]]]].
>> self pop: 2 thenPushFloat: result
>>
>> Right?
>>
>
> I was focused on image side fallback code and came up to more or less the
> same
> So it sounds very good to me :)
>
> We still have a compiler warning for long -> int,.
> For sure, we can ignore it this time...
> But this kind of false warning is pesky.
> The risk is to drown a true positive into a flow of false ones...
>
> Using an argument > 2^31 is more theoretical than practicle. It should be
> very rare.
> So once we have image side for handling this rare case, I wonder if it is
> really necessary to complexify the primitive...
> And it's good anyway to have a fallback code, it makes the primitives
> optional and lower the barrier for guys doing experiments like Bert.
> So I would say let the primitive fail, and eliminate the warning.
>
I was thinking the same thing. So something like
(objectMemory bytesPerOop > 4
and: [arg > (1 << 30) or: [arg < (-1 << 30)]]) ifTrue:
[^self primtiiveFail]
and then cast the arg to (int) to avoid the warning. I'll make it so
tomorrow.
>
>
>
>> On Wed, Feb 11, 2015 at 5:57 PM, Eliot Miranda <eliot.miranda at gmail.com>
>> wrote:
>>
>>> Hmm, close, but I need to check explicitly for zero. Here's the version
>>> for the FloatMathPlugin (& with more conventional formatting :-) ):
>>>
>>> primitiveTimesTwoPower
>>> "Computes E raised to the receiver power."
>>> | rcvr arg twiceMaxExponent result |
>>> <export: true>
>>> <var: #rcvr type: #double>
>>> <var: #result type: #double>
>>> arg := interpreterProxy stackIntegerValue: 0.
>>> rcvr := interpreterProxy stackFloatValue: 1.
>>> (interpreterProxy failed) ifTrue:
>>> [^nil].
>>> ((self isFinite: rcvr) and: [rcvr ~= 0.0])
>>> ifFalse:
>>> [result := rcvr]
>>> ifTrue:
>>> [twiceMaxExponent := 2 * (1 << 11).
>>> arg < twiceMaxExponent negated
>>> ifTrue:
>>> [result := 0.0]
>>> ifFalse:
>>> [arg > twiceMaxExponent
>>> ifTrue:
>>> [result := 1.0e200 / 1.0e-200]
>>> ifFalse:
>>> [result := self cCode: '__ieee754_ldexp(rcvr, arg)'
>>> inSmalltalk: [rcvr timesTwoPower: arg]]]].
>>> (self isnan: result) ifTrue:
>>> [^interpreterProxy primitiveFail].
>>> interpreterProxy
>>> pop: interpreterProxy methodArgumentCount + 1;
>>> pushFloat: result
>>>
>>> On Wed, Feb 11, 2015 at 5:43 PM, Eliot Miranda <eliot.miranda at gmail.com>
>>> wrote:
>>>
>>>> and of course I meant
>>>>
>>>> primitiveTimesTwoPower
>>>> | rcvr result arg twiceMaxExponent |
>>>> <var: #rcvr type: #double>
>>>> <var: #result type: #double>
>>>> arg := self stackTop.
>>>> (objectMemory isIntegerObject: arg) ifFalse:
>>>> [^self primitiveFailFor: PrimErrBadArgument].
>>>> rcvr := objectMemory floatValueOf: (self stackValue: 1).
>>>> (self isFinite: rcvr) ifFalse:
>>>> [result := rcvr] ifTrue:
>>>> [arg := objectMemory integerValueOf: arg.
>>>> twiceMaxExponent := 2 * (1 << self floatExponentBits).
>>>> arg < twiceMaxExponent negated ifTrue:
>>>> [result := 0.0] ifFalse:
>>>> [arg > twiceMaxExponent ifTrue:
>>>> [result := 1.0e200 / 1.0e-200] ifFalse:
>>>> [result := self cCode: [self ld: rcvr exp: arg]
>>>> inSmalltalk: [rcvr timesTwoPower: arg]]]].
>>>> self pop: 2 thenPushFloat: result
>>>>
>>>> On Wed, Feb 11, 2015 at 5:41 PM, Eliot Miranda <eliot.miranda at gmail.com
>>>> > wrote:
>>>>
>>>>> Hi Nicolas,
>>>>>
>>>>> On Wed, Feb 11, 2015 at 2:57 PM, Nicolas Cellier <
>>>>> nicolas.cellier.aka.nice at gmail.com> wrote:
>>>>>
>>>>>>
>>>>>> Some C functions in libm only take an int, not a long.
>>>>>> In 32 bits int=long, so no problem.
>>>>>> In 64 bits generally int=32 bits, long=64 bits, so casting a long to
>>>>>> int might lead to catastrophic loss and unexpected behavior.
>>>>>>
>>>>>> This is the case for example in primitiveTimesTwoPower
>>>>>> | rcvr arg |
>>>>>> <var: #rcvr type: #double>
>>>>>> arg := self popInteger.
>>>>>> rcvr := self popFloat.
>>>>>> self successful
>>>>>> ifTrue: [ self pushFloat: (self cCode: 'ldexp(rcvr, arg)'
>>>>>> inSmalltalk: [rcvr timesTwoPower: arg]) ]
>>>>>> ifFalse: [ self unPop: 2 ]
>>>>>>
>>>>>> arg will be a long in Spur64, won't it?
>>>>>> but ldexp only takes an int
>>>>>> double ldexp(double x, int exp);.
>>>>>>
>>>>>> So guess what if we call (1.0 timesTwoPower: 16r10000000001)...
>>>>>> Normally there should be a C compiler warning, and we should care of
>>>>>> it.
>>>>>>
>>>>>> To solve this, maybe we need a
>>>>>>
>>>>>> <var: #arg type: #int>
>>>>>> arg := self signed32BitValueOf: stackTop.
>>>>>>
>>>>>
>>>>>
>>>>> I think we can check intelligently. We know that for a finite
>>>>> non-zero value, ldexp(value, n) is infinite if n > (2 * max exponent) and
>>>>> zero if n < (2 * max exponent), that for a NaN value, ldexp(value, n) is
>>>>> NaN and for an infinite value ldexp(value,n) is infinite, and for a 0
>>>>> value, it is zero. So I propose
>>>>>
>>>>> primitiveTimesTwoPower
>>>>> | rcvr result arg twiceMaxExponent |
>>>>> <var: #rcvr type: #double>
>>>>> <var: #result type: #double>
>>>>> arg := self stackTop.
>>>>> (objectMemory isIntegerObject: arg) ifFalse:
>>>>> [^self primitiveFailFor: PrimErrBadArgument].
>>>>> rcvr := objectMemory floatValueOf: (self stackValue: 1).
>>>>> (self isFinite: rcvr) ifFalse:
>>>>> [result := rcvr] ifTrue:
>>>>> [arg := objectMemory integerValueOf: arg.
>>>>> twiceMaxExponent := 2 * (1 << self floatExponentBits).
>>>>> arg < twiceMaxExponent ifTrue:
>>>>> [result := 0.0] ifFalse:
>>>>> [arg > twiceMaxExponent ifTrue:
>>>>> [result := 1.0e200 / 1.0e-200] ifFalse:
>>>>> [result := self cCode: [self ld: rcvr exp: arg]
>>>>> inSmalltalk: [rcvr timesTwoPower: arg]]]].
>>>>> self pop: 2 thenPushFloat: result
>>>>> --
>>>>> best,
>>>>> Eliot
>>>>>
>>>>
>>>>
>>>>
>>>> --
>>>> best,
>>>> Eliot
>>>>
>>>
>>>
>>>
>>> --
>>> best,
>>> Eliot
>>>
>>
>>
>>
>> --
>> best,
>> Eliot
>>
>>
>
>
--
best,
Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20150211/a967394e/attachment-0001.htm
More information about the Vm-dev
mailing list