FAQ: Why does a value look more precise when displayed in MATLAB or Simulink than in the generated code?

11 views (last 30 days)
A value used in my design looks more precise when displayed in MATLAB or Simulink.
The same value in C or C++ generated by Embedded Coder looks less precise.
For example
format long
u = single(7.5066509)
u = single
7.5066509
The same value appears in the generated code as
float u = 7.506651F;
Why is that?

Accepted Answer

Andy Bartlett
Andy Bartlett on 15 Dec 2022
Edited: Andy Bartlett on 6 Dec 2023
Textual Representation is Often an Approximation
The textual representation of values displayed in MATLAB and Simulink is often an approximation of the value that is actually represented. To show the EXACT value represented in a numeric type can often be quite messy. It can even be exceedingly messy.
Example exact represented value is really messy
format long
v = eps(0)
v =
4.940656458412465e-324
eps(0) looks a bit messy when displayed in format long. But even that is an approximation.
The EXACT value represented in double requires over 750 digits to represent in decimal scientific notation.
vSym = sym(v,'f');
pow10Exp = floor(log10(abs(vSym)));
mantissaSym = vSym / (sym(10)^pow10Exp);
mantissaStr = char( vpa(mantissaSym, 10000) )
mantissaStr = '4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625'
lenMantissaStr = length(mantissaStr)
lenMantissaStr =
752
fprintf('v = 10^%d * %s\n',pow10Exp, mantissaStr)
v = 10^-324 * 4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625
Faithful Numeric Agreement is Hugely Important
MathWorks Coders make it a priority for the numerics in MATLAB and Simulink to be faithfully matched by the C/C++/HDL code generated by Coders. MathWorks users get huge productivity boosts by detecting behavioral issues early in their workflows in the convienient and "safe" environment provided by MATLAB and Simulink. For integer and fixed-point, the expected agreement is bit-true. For floating-point, Coders try very hard to get as close as is reasonable to bit-true. A place where bit-true is definitely achieved for floating-point, fixed-point, and integer is the generation of code for constants.
Round-Trip Lossless is "Exact Enough" for Bit-True Constants in Generated Code
Since textual representations of values are very often approximations of the exact value represented in a numeric type, you may be wondering how Coders can achieve bit-true agreement with MATLAB and Simulink for constants.
The key is computer languages like C, C++, and MATLAB parse the text and map it to the closest representable value in the numeric type being used.
So Coders don't need to generate the whole messy text for the EXACT value. Coders just need to generate text that is close enough to the exact value such that the language's parser will map that text back to the identical value that was represented in MATLAB and Simulink.
In other words, there has to be a lossless round trip
  1. EXACT value FOO represented in memory in MATLAB or Simulink
  2. "Exact enough" textual representation of the value FOO is created
  3. C, C++, HDL, etc. parser converts that text to a value in machine code / memory that is EXACTLY the same as the original value FOO.
Infinite choices for "Exact Enough" Text
As you know, there are an infinite number of values that will round to a value such as 3. When rounded to nearest representable integer, all the infinite values in the open interval (2.5, 3.5) will definitely round to 3.
Similarly, for a given representable value in a given numeric type, there are an infinite number of textual representations of the value that will parse back to the original given representable value. Proper parsing will provide a round to nearest representable behavior.
Let's consider the value provided in the original question, single(7.5066509).
The exact representation of that value and the two nearest representable values are the following.
valueNextRepAbove: '7.506651401519775390625'
valueExactRepr: '7.5066509246826171875'
valueNextRepBelow: '7.506650447845458984375'
Text will be "exact enough" for round-trip lossless handling if the symbolic interpretation of the textual value is closer to the middle of these three values than it is to the other two values. So if the textual value is between the two mid-points of this sequence of three values, then a correctly implemented parser will map that text to the desired exact value 7.5066509246826171875.
Example of "Exact Enough" Round-Trip Lossless Text
Utilizing the two attached functions, let's probe the value from the question.
u = single(7.506651)
u = single
7.5066509
r = valueToRoundTripString(u)
r = struct with fields:
value: 7.5066509 valueExactRepr: '7.5066509246826171875' valueExactLog2Str: '3935647 / 2^19' log2MantissaInt: 3935647 log2Exponent: -19 numerictypeStr: 'numerictype('single')' numerictype: [1×1 embedded.numerictype] hardwareProp: [1×1 struct] textForMATLAB: 'single(7.506651)' valueNextRepAbove: '7.506651401519775390625' valueMidpointToRepAbove: '7.5066511631011962890625' textForC: '7.506651F' valueMidpointToRepBelow: '7.5066506862640380859375' valueNextRepBelow: '7.506650447845458984375'
The 2nd to last field of the output structure shows one example of round trip lossless text for the original value. Notice that the text is clearly between the nearest representable mid-points.
valueMidpointToRepAbove: '7.5066511631011962890625'
textForC: '7.506651F'
valueMidpointToRepBelow: '7.5066506862640380859375'
Two examples of the many other C textual values that would also be round trip lossless are the following.
7.50665105F
7.5066507F
Any of the infinite values in the open interval
(7.5066506862640380859375, 7.5066511631011962890625)
will definitely map to the desired value 7.5066509246826171875 if the language's parser is correctly implemented.
Short is nice, but Shortest is NOT a priority
The shortest possible text that is round trip lossless is definitely appealing. When Coders generate text for a constant, that text will very often, but not always, be the shortest possible text. Sometimes the text can be a character or two longer than needed. It's nice to be shorter, but a few extra characters now and then have no impact on the faithful numeric agreement between the generated code and MATLAB and Simulink.
Doing the "optimization search" to find the exact shortest text can be time consuming. Many MathWorks user have made it clear that they value having Coders complete the code generation process quickly. This user feedback made it clear that productivity of users was more important that getting the absolute fewest characters 100% of the time.
Round trip lossless representation of constants is an absolute priority. Coders run lots of testing everyday to make sure that remains true. As mentioned above, numeric fidelity is a big productivity boost to users of MATLAB, Simulink, and Coders.
Shortest is not always unique
The shortest possible text that is round trip lossless is not always unique.
u = single(423350.16)
u = single
4.2335016e+05
r = valueToRoundTripString(u,'single')
r = struct with fields:
value: 4.2335016e+05 valueExactRepr: '423350.15625' valueExactLog2Str: '13547205 / 32' log2MantissaInt: 13547205 log2Exponent: -5 numerictypeStr: 'numerictype('single')' numerictype: [1×1 embedded.numerictype] hardwareProp: 'single' textForMATLAB: 'single(423350.16)' valueNextRepAbove: '423350.1875' valueMidpointToRepAbove: '423350.171875' textForC: '423350.16F' valueMidpointToRepBelow: '423350.140625' valueNextRepBelow: '423350.125'
The shortest round trip lossless representation of this value uses 8 decimal digits.
But the text is not unique, three different text representations would all parse back to the same original value.
423350.15F
423350.16F
423350.17F
As an extreme example, consider the following example.
u = eps(single(0))
u = single
1.4012985e-45
r = valueToRoundTripString(u,'single')
r = struct with fields:
value: 1.4012985e-45 valueExactRepr: '1.4012984643248170709237295832899161312802619418765157717570682838e-45' valueExactLog2Str: '1 / 2^149' log2MantissaInt: 1 log2Exponent: -149 numerictypeStr: 'numerictype('single')' numerictype: [1×1 embedded.numerictype] hardwareProp: 'single' textForMATLAB: 'single(1E-45)' valueNextRepAbove: '2.8025969286496341418474591665798322625605238837530315435141365677e-45' valueMidpointToRepAbove: '2.1019476964872256063855943749348741969203929128147736576356024258e-45' textForC: '1.0E-45F' valueMidpointToRepBelow: '7.0064923216240853546186479164495806564013097093825788587853414194e-46' valueNextRepBelow: '0'
This value has four distinct one digit representations
8e-46F
9e-46F
1e-45F
2e-45F
All four textual representations will parse back to eps(single(0)).
MATLAB Parser Plays by the Same Rules
Just like C, MATLAB has a parser that reads text that users like you and I write.
An infinite number of textual representations will parse to the same value.
vec = sort([
single(7.506651)
single(7.5066510000789)
single(7.5066507)
single(7.5066506900111111)
])
vec = 4×1
7.5066509 7.5066509 7.5066509 7.5066509
distinceValues = unique(vec)
distinceValues = single
7.5066509
All that different text really only produced one distinct value in single precision floating point. Longer text did NOT mean a precision difference.
Again, any textual representation in this interval
(7.5066506862640380859375, 7.5066511631011962890625)
when mapped in to single precision floating point will produce the EXACT value represented in single of 7.5066509246826171875.
Same Principles Apply to All Numeric Types
The question and examples have focused on singles, but don't draw a false conclusion that this is distinct to singles. The same principles apply to doubles, half, integer, fixed-point and so on.
For example, for the type double, any textual value in the open interval
(7.506650999999999296363739631487987935543060302734375, 7.506651000000000184542159331613220274448394775390625)
will be parsed by C, MATLAB, etc, to the EXACT representable value
7.5066509999999997404529494815506041049957275390625
one example of "exact enough" text is
7.506651
Takeaway "exact enough" text is what matters
In MATLAB, Simulink, C, etc., any text that is "exact enough" for the given type is equivalent. It may look different, but if it is "exact enough" it leads to the same representable value of the specific type being used.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!