Subject: |
Re: Conversion problem from double to 32 bit unsigned int (in ao) |
From: |
Andrew Johnson <[email protected]> |
To: |
[email protected] |
Date: |
Thu, 11 Apr 2013 15:37:35 -0500 |
Hi Dirk,
On 2013-04-10 Dirk Zimoch wrote:
> Today I found a strange problem. I have a device that has 32 bit
> unsigned int output registers. I want to scale an analog value (e.g.
> -10.0 .. +10.0) to the raw range 0x00000000 ... 0xffffffff. The
> special_linconv() function of the ao record sets ESLO and EOFF correctly
> (ESLO=20/4294967295=4.65661287416E-09, EOFF=-10.0). All negative values
> work but any positive value gives wrong results. Even worse, the results
> are different on different machines: 0x80000000 on linux-x86, 0x7fffffff
> on vxworks-ppc604 for all positive values.
>
> What happens? In the convert() function the analog (double) value is
> converted to epicsInt32 (type of the RVAL field). However if the double
> value is too large for the integer, strange and obviously implementation
> dependent things happen. I cannot find anything about this case in K&R.
Benjamin quoted the wrong part of K&R §A6.3 because we're not dealing with
unsigned types here at all. The first sentence of that section ends "if the
resulting value cannot be represented in the integral type, the behavior is
undefined", which explains why you're getting different results on different
machines.
The problem is that with your ESLO and EOFF values the negative engineering
values -10.0 .. <0 convert to the RVAL range 0 .. 0x7fffffff but 0x7fffffff is
the largest possible value of an epicsInt32, so there's no range left for zero
and the positive values to map to (you want it to convert 0.0 to 0x80000000
but as a signed number that's the other end of the number range).
You need to offset everything so that the double-to-int32 conversion converts
0.0 => 0x0 which means setting EOFF = 0.0 and using the ROFF field to bring
the 0.0 value back to the right place afterwards. For a UINT32 output
register your device support should thus initialize ROFF to 0x80000000.
Now strictly speaking even this triggers Undefined Behavior because the record
relies on a signed integer overflow in the subtraction of ROFF to convert the
positive engineering values into the right part of the number range, but in
practice I believe this still works on all our platforms. Compiler optimizers
are relying on more and more obscure rules that cause UB to break previously
working code so this might break one day, but gcc does have a flag -fwrapv to
tell it to allow signed overflows.
> The same thing can happen at other locations, too.
> Example:
>
> record (mbbo, "DZ:mbbo") {
> field (FLNK, "DZ:calcout")
> }
> record (calcout, "DZ:calcout") {
> field (INPA, "DZ:mbbo")
> field (CALC, "A*65536")
> field (OUT, "DZ:longout PP")
> }
> record (longout, "DZ:longout") {
> }
>
> Writing 0xffff to the mbbo results in 0x8000000 on my 32 bit Linux PC.
longout.VAL is epicsInt32 which can't hold the value 4294836225, so you're
getting an overflow when doing the conversion.
> field (CALC, "A<<16") on the other hand gives the expected result
> 0xffff0000.
You're probably relying on UB there again. In C99 and C++0x you are not
allowed to shift a 1-bit into or past the sign bit when shifting a signed
integer left-wards. Apparently the rules changed for those standards though,
I'm not sure what the earlier standards actually said in that case.
There's a guy called John Reghr, a professor at the University of Utah, who
writes about undefined behaviors in C and C++ code quite a lot; I recommend
reading his stuff. He ran an interesting competition recently to write a
simple ascii-to-long numeric converter that must not exhibit UB, so no
overflows allowed but it must detect numbers that won't fit into the long.
- Andrew
--
It is difficult to get a man to understand something, when his salary
depends upon his not understanding it. -- Upton Sinclair
- Replies:
- Re: Conversion problem from double to 32 bit unsigned int (in ao) Benjamin Franksen
- References:
- Conversion problem from double to 32 bit unsigned int (in ao) Dirk Zimoch
- Navigate by Date:
- Prev:
Re: Conversion problem from double to 32 bit unsigned int (in ao) Benjamin Franksen
- Next:
Re: Conversion problem from double to 32 bit unsigned int (in ao) Benjamin Franksen
- Index:
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
<2013>
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
- Navigate by Thread:
- Prev:
Re: Conversion problem from double to 32 bit unsigned int (in ao) Benjamin Franksen
- Next:
Re: Conversion problem from double to 32 bit unsigned int (in ao) Benjamin Franksen
- Index:
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
<2013>
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
|