Incorrect conversion when decimal point embedded in VT_BSTR and German locale used - c++

I have a piece of code(c++) that is writing some floating point values to excel like this:
...
values[ position ].bstrVal = formattedValue;
values[ position ].vt = VT_BSTR;
...
as you can see those floating point values are stored in the form of string and the decimal point is formatted in different ways, for example:
"110.000000", "20.11" etc. (this example is for English locale)
Now it works perfectly when English locale is used. However when I switch to German locale in the Control Panel the decimal point is changed to "," (and that's fine) but after passing those localized strings to Excel they are not correctly converted. For example in case of writing "110,000000" I'm getting 100 millions in excel. Other values like "20,11" stay as a text.
The only way to fix this is to overwrite the decimal point with "." in my program before writing to Excel. Any ideas why the conversion is not locale-aware when using VT_BSTR?
I should also add that I tried to switch the locale in my program from default one to German - still no luck.
Thank you in advance

It is never a good idea to let Excel guess at the value type. Do not use VT_BSTR, a currency value should be of variant type VT_CY. Assign the cyVal member with the value. It is an 8 byte integer value (int64 member of type LONGLONG), the currency amount multiplied by 10,000. Ten thousand :)

Related

powerquery: extra digits added to number when importing table

Glad to ask a question here again after more than 10 years (last one was about BASH scripting, now as I'm in corporate, guess what... it's about excel ;) )
here it's my question/issue:
I am importing data with powerquery for further analysis
I have discovered is that the values imported contains extradigits not present in the original table.
I have googled for this problem but I have not been able to find an explanation nor a solution ( a similar issue is this one this one , more than one year old, but with no feedback from Microsoft )
(columns are formatted as text in the screenshot but the issue is still present even if formatted as number)
The workaround I am using now, but I am not happy with that is the following:
I "increased decimal" to make sure all my digits are captured (in my source the entries do not have all the same significant digits),
saved as csv
imported impacted columns as number
convert columns as text (for future text match
I am really annoyed by this unwanted and unpredictable behaviour of excel.
I see a serious issue of data integrity, if we cannot rely on the powerquery/powerbi platform to maintain accurate queries, I wonder why would be use it
adding another screenshot to clarify that changing the source format to text does not solve the problem
another screenshot added following #David Bacci comments:
I think I wrongfully assumed my data was stored as text in the source, can you confirm?
If you are exporting and importing as text, then this will not happen. If you convert to number, you will lose precision. From the docs (my bold):
Represents a 64-bit (eight-byte) floating-point number. It's the most
common number type, and corresponds to numbers as you usually think of
them. Although designed to handle numbers with fractional values, it
also handles whole numbers. The Decimal Number type can handle
negative values from –1.79E +308 through –2.23E –308, 0, and positive
values from 2.23E –308 through 1.79E + 308. For example, numbers like
34, 34.01, and 34.000367063 are valid decimal numbers. The largest
precision that can be represented in a Decimal Number type is 15
digits long. The decimal separator can occur anywhere in the number.
The Decimal Number type corresponds to how Excel stores its numbers.
Note that a binary floating-point number can't represent all numbers
within its supported range with 100% accuracy. Thus, minor differences
in precision might occur when representing certain decimal numbers.
BTW, you should probably accept some of the good answers from your previous questions from 10 years ago.

How to use numbers present as text with different unit prefixes in calculations

I have data in a spreadsheet describing amount of data transferred over a mobile network: data in one column (over 300 rows) has three possible forms:
123,45KB
123,45MB
1,23GB
How can I transform or use this data in order to sum or do other calculations on numbers properly?
Assuming your data is in column A and there are always two characters as unit ("KB", "MB" or "GB") at the end, then the formula for transforming the data to numeric could be:
=--LEFT(A2;LEN(A2)-2)*10^(IF(RIGHT(A2;2)="KB";3;IF(RIGHT(A2;2)="MB";6;IF(RIGHT(A2;2)="GB";9))))
Result:
Put the formula in B2 and fill downwards as needed.
I suspected the decimal delimiter in your locale is comma. If not, please state what it is.
Also since this site is English, I have used English function names. Maybe you need to translate them into your language version.
If the decimal delimiter in your locale is not comma, then you need substituting the comma with your decimal delimiter to get a proper numeric decimal value.
For example if the decimal delimiter is dot, then:
=SUBSTITUTE(LEFT(A2,LEN(A2)-2),",",".")*10^(IF(RIGHT(A2,2)="KB",3,IF(RIGHT(A2,2)="MB",6,IF(RIGHT(A2,2)="GB",9))))
An alternative formula:
=LEFT(A1,LEN(A1)-2)*10^(3*MATCH(RIGHT(LEFT(A1,LEN(A1)-1)),{"K","M","G"},0))
Uses the position of the next to last character in an array to determine the factor.

Output is '*' when writing a real to a string

I have a real CURRENTTIME I want to convert to a string named TIMEDIR. As TIMEDIR has to change size it is allocatable. As far as I could find out, the allocation works fine. Also, I checked that CURRENTTIME has a value.
ALLOCATE(CHARACTER(LEN=1)::TIMEDIR)
WRITE(TIMEDIR, '(F1.0)') CURRENTTIME
But
WRITE(*,*) TIMEDIR
outputs *, where it should be 0 (CURRENTTIME is 0.0000000). I have no clue what the problem is.
You're writing the output as a floating point number. Floating point numbers always have a decimal point or an exponent to differentiate them from integers. Thus the narrowest output of a float possible is 0., i.e. 2 characters, and a format of F1.0 will always result in a "*" being printed as the field width is insufficient for what is being written.
Ian Bush's answer says what you need to know: output for a real value using the F edit descriptor requires a field width of at least 2. I'll elaborate a bit on some other aspects.
You mention
As TIMEDIR has to change size it is allocatable
but in the code fragment we see
WRITE(TIMEDIR, '(F1.0)') CURRENTTIME
This suggests a little misunderstanding. [It may be that there's no confusion, but I'll labour the point for the benefit of any other reader coming to the question.]
When an output format looks like Fw.d for w greater than zero the width of the output field is always w. This "always" means: whatever the value of the corresponding variable, the effect of the write statement above on TIMEDIR is to have a single character non-blank.
Now, as in that other answer, 2 is the minimum field width for output of a real value[1]. As with all other numeric output formatting, if the field isn't wide enough for the corresponding value the field consists entirely of *s. F1.0 will always result in output *. If you want output 0. (or 0,)[2] you'll need F2.0.
Coming back to the "varying size of TIMEDIR", output format F2.0 is (possibly) sufficient for non-negative values of CURRENTTIME less than 10, but for negative values or values not less than 10 it isn't. It may well be that this is where F0.d comes in. It's only with this form of the F edit descriptor that the width of the field depends on the output value. That's probably an answer to another question, though.
Finally, as you mention
I have to find out how to make "0" out of "0."
I'll point out that you're looking at having to do some additional logic, such as mentioned elsewhere.
1 And 2 may not be sufficient, even for a zero value: print '(SP,F2.0)', 0.
2 The choice of 0. and 0, depends on the decimal mode: print '(DC,F2.0,DP,F2.0)', 0., 0.

What is SAS format 8.

I am new to SAS and currently working on a small piece of work with SAS.
Could I please ask what the below format means? I believe the 8. is formatting two digits to the right of the decimal place such as 896.33 but I am not sure. Not really sure what input means.
input(tablename.fieldname, 8.)
That is an INFORMAT, not a FORMAT. It means to read the first 8 characters as a number. If there is a decimal point in the data then it is used naturally. You could have up to 7 digits to the right of the decimal point (since the decimal point would use up the eighth character position). It will also support reading scientific notation so '896.33E2' would mean the number 89,633.

Subtracting Numbers larger than 18 digits in length

Ok this is a tough one or else a stupid one but it has me stumped. I am working with serial numbers in MSSQL and they are stored in the database as nvarchar(50) and to do subtracting calculations on them I use the following query to convert them to the data-type BIGINT and subtract as normal.
SELECT
SUM(
CAST(second_Serial_Nb AS BIGINT)-CAST(Serial_Nb AS BIGINT))
FROM [TEST].[dbo].[Serial_Table]
WHERE ID = '3'
this query works fine for serial numbers up to 18 digits in length, but as soon as I increase there size of the serial numbers to 20 digits in length I get the error that the numbers can not be converted to data-type bigint
Msg 8815, Level 16, State 2, Line 2
Arithmetic overflow error converting expression to data type bigint
Is there a work around using a different number data type like hexi or something. I am also using C++ maybe I could create a function there instead of SQL?
Any comments or suggestions greatly appreciated, Thanks for reading.
BIGINT is just a normal, 64-bit integer. It is not an arbitrary-precision integer.
If you want to store more information, you can either keep it in string form, or use a NUMERIC or DECIMAL type; both solutions are of course much slower than a native, fixed-width integer.