Excel VBA unexpected truncation - excel

So I've spent the better part of the day debugging my VBA Class module and came upon a worrying phenomenon.
On the main spread sheet I have a cell formatted as "currency", with 9 decimal place precision. In my VBA class, I have an attribute (a double) which takes on that cell value upon instantiation. i.e.,
myClass.loanDefault_7 = Range("loanDefaults")(1,7)
However, when I check the value stored in myClass.loanDefault_7 after the assignment, it has only 4 decimal place precision.
My question is why is VBA automatically (without my permission) truncating down this value? I want to understand why this happens, and how I can prevent it in the future.
Any help is much appreciated!

Try using the Value2 property instead:
myClass.loadDefault_7 = Range("loadDefaults")(1,7).Value2
From MSDN:
The only difference between this property and the Value property is that the Value2 property doesn’t use the Currency and Date data types. You can return values formatted with these data types as floating-point numbers by using the Double data type.
It seems that the Value property will automatically coerce the value into a Currency data type (which, AFAIK, is only capable of representing 4 numbers to the right of the decimal point).

Related

Trying to show only a certain amount of numbers

To make the sale to my customer I need to import numbers from a report into an Excel document. For example the number coming in will be 14.182392. The only reason for my guy not to buy the product is because he only wants to view 14.182 on the Excel sheet. Okay so the other catch is, the number CANNOT be rounded in any shape or form.
So what I need is a way to just show so much of number, WITHOUT ROUNDING.
Is this possible? Any ideas of how I could get around this would be fantastic.
Please try:
=TEXT(INT(A1)+VALUE(LEFT(MOD(A1,1),5)),"00.000")
Firstly =TRUNC is a better answer (much shorter). My version was connected with uncertainty in your requirement (it is odd!) and in the hope it might be easier to adjust if not exactly what you/your boss wanted.
TRUNC literally just truncates the decimals (no rounding!) to a length to suit (ie 3 if to show nn.182 given nn.182392 or say nn.182999).
LEFT may also be a better choice, but that depends upon knowing how large the integer part of your number is. =LEFT(A1,6) would display 14.189 given say 14.189999 in A1. However it would show 1.4189 given 1.4189999 in A1 (ie four decimal places).
The formula above combines text manipulation with number manipulation.:
INT takes just the integer value (here 14.)
MOD takes just the modulus – the residual that is not an integer after division, in this case by 1. So just the .182392 part. LEFT is then applied here in a similar way to as used above, but without needing to concern oneself with the length of the integer part of the source value (ie 14 or 1 etc does not matter).
VALUE then converts the result back into numeric format (string manipulation functions such as LEFT always return text format) so our abbreviated decimal string can then be added to our integer.
Finally, the TEXT part is for formatting but is hard or impossible to justify! About the only use is that it displays the result left-justified in the cell – perhaps a little warning that the number displayed is not the “true” value (eg it won’t SUM) because, as a result of a formula, it won’t be marked with a little green warning triangle.
The displayed values can use the TRUNC function like this,
=TRUNC(A1, 3)
But you must use A1 in any calculations to retain the precision of the raw value.
Easiest way I know:
=LEFT(A1; x)
where x = the amount of characters You want. Mind that the dot counts as a character as well.

When does FlexCel's TCellValue.IsDateTime always return false?

I am using TMS' FlexCel Excel document component. I am - however - having a problem with dates. As I understand it, Excel has three types: Strings, numbers and datetimes. This is also represented in FlexCel's TCellValue's methods, such as IsString, IsNumber and IsDateTime.
However, IsDateTime always returns False. TCellValue has a ToDateTime function that will always return a TDateTime. If the original cell value is not a date, the time will be "1899-12-31T23:59:59".
I am however not keen on the idea using DateUtils's YearOf() on 1899 to detect whether it truly was a date. What if the cell contained a float (double)? That would become a proper TDateTime.
I have made sure that the format for these cells are a datetime. When exporting to Excel's XML Spreadsheet 2003 format, I can see that the cells are properly detected as datetimes by Excel. Why doesn't FlexCel detect the same thing?

Using Apache POI to manipulate excel files

I'm trying to write a test using the Marathon testing tool with Jython. I'm using the Apache POI in order to read/write with Excel files. I'm very new to Jython and the Apache POI so this question may seem very simple to some, but I can't get past it. I'm using the getCell() function in the Cell interface and it grabs the cell just fine, but the contents that it prints for me are not what I want. I want the integer value, but it returns a floating point.
for r in range(1, rowsBusiness):
row = sheetBusiness.getRow(r)
idNum = row.getCell(0) # it is returning double values here
print idNum
print idNum.getStringCellValue()
I'm okay with it returning double values so long as I can convert them to a string or integer because the application that I'm testing converts from string to integer or spits out an error, but I can't figure out how to convert from double and get rid of the decimal point. The getStringCellValue() function doesn't work on idNum. It just leaves it blank and the test gets stuck. I also formatted the Excel file so that it only takes integer values in the cells that I'm referring to. So, for example, in the excel file I have the value 1(formatted to not contain any decimal points), but the print idNum returns 1.0
Any helpful hints on how to get this to a string or integer? Or any other ideas that might contribute to a successful workaround?
The Excel file format only has a few basic types, like Numeric and String. Most numbers get stored as a floating point value + formatting rules. That's why you're getting back a double not an int - that's simply what lives in the file!
Apache POI provides the DataFormatter class, which takes a floating point value and the format string, and does its best to return a string of the number as shown in Excel. For your case, calling that is likely to yield what you're after.

VBA writing to file is rounding numeric values - how to prevent?

In the below code I am having problems making sure the file writer does not round my number off to a certain number of decimal places. I need to use a variant because sometimes the value is string and at other times it is a number.
How can I force it to write exactly what the variable is? For example the below code might show 0.00038 and I want to show the exact value.
Dim MyFile1 As Variant
Dim MyNumber as Variant
MyFile0 = "C:/myfile.csv"
fnum0 = FreeFile()
Open MyFile0 For Output As fnum0
MyNumber = 0.0003759656
Print #fnum0, MyNumber
Your code is fine. The issue is with excel. When opening a CSV in excel, including thru VBA, excel detirmines what a cell is. Typically, if it lloks like a number with more then 5 characters it will express it in Scientific notation or rounded to 5 places.
Note sure what you are doing with the CSV file before or where you are getting it from, but here are some options to prevent excel from changing your data:
In VBA use the Workbook.OpenText command to open the CSV with either:
-the all excel cells as text*
-the column stored number with so many decimal places
(note a max of 30 decimal places apply)
-a particular column store as text.
--A full list of option syntax here http://msdn.microsoft.com/en-us/library/office/bb22351(v=office.12).aspx
You may also import your CSV into an excel spreadsheet, it will give you the option to choose data type for each column. Then run the VBA against the excel file.
If you are not doing formulations in excel, I would recomend storing the number as a text string. VBA automatically converts strings of numbers into numeric values when needed.
It's likely that you're experiencing floating point errors. They are are very small errors that occur when numbers are converted from/to base 10 (human) to/from base 2 (computer). For your purposes, you need to determine what it means that two values are not equal. It's not just val1 <> val2 because that won't account for the tiny errors. If you're dealing with money, for instance, you probably don't care about anything less than a penny. So you might determine inequality as ABS(val1 - val2) > .001. You just need to determine what your tolerance for equality is and compare to that standard.
It may be that your number is not being stored as a number in vba, and when you write it Excel converts it. Say you're number is 1.789 and you write it to a cell that is formatted as 'GENERAL'. Excel will write 1.79 (and think that's the number, not even a formatting issue).
What I've discovered, is if you convert the number to a decimal first CDEC(YOURNUMBER), it will write it correctly. For good measure, I also verify that the cell is '.NUMBERFORMAT = "GENERAL'

Excel OleDb Data Types

Does anyone have a list of data types I can use in a CREATE TABLE statement for Excel? I have searched and cannot come up with anything; at least not anything official. From one place, I found:
CHAR(255)
MEMO
INT
DATE
TIME
What about other numeric types? DECIMAL, etc? DATETIME (together)?
Thanks in advance.
Be careful: Excel natively supports only the the following data types (so any other data type will be converted to one of these)
Double precision floating point (this handles integers, longs, dates, times, currency etc) string Boolean Error
These data types can be formatted by the Excel renderer to look like currency, integer, date time etc.

Resources