Keep precision while converting string to decimal - doctrine

I have a database with columns of string type with decimal values in them. Those decimals are longitudes and latitudes, so these numbers can be long of 8 units after the dot.
I want to convert the type from string to decimal. The problem is that when I assign decimal type in my schema.yml, it rounds the values to 2 units after the dot, and if I had a scale parameter of 12 and size of 16 (to be large), it rounds it to 2 units after the dot and adds 10 zeros at the end.
lat: { type: string(255), notnull: false }
to
lat: { type: decimal, scale: 12, size:16, notnull: false }
Is there a way to migrate these columns to decimal type without losing precision?

Do you really need a decimal?
Because regarding an old ticket on the Doctrine bug tracker, it seems better to use a float instead of a decimal for this kind of case.
Give it a try.

Related

How to read percentage as decimal number?

I'm trying to find the decimal value from a percentage that a user inputs.
For example, if a user inputs "15", i will need to do a calculation of 0.15 * number.
I've tried using .to_f, but it returns 15.0:
15.to_f
#=> 15.0
I also tried to add 0. to the beginning of the percentage, but it just returns 0:
15.to_s.rjust(4, "0.").to_i
#=> 0
Divide by 100.0
The easiest way to do what you're trying to do is to divide your input value by a Float (keeping in mind the inherent inaccuracy of floating point values). For example:
percentage = 15
percentage / 100.0
#=> 0.15
One benefit of this approach (among others) is that it can handle fractional percentages just as easily. Consider:
percentage = 15.6
percentage / 100.0
#=> 0.156
If floating point precision isn't sufficient for your use case, then you should consider using Rational or BigDecimal numbers instead of a Float. Your mileage will very much depend on your semantic intent and accuracy requirements.
Caveats
Make sure you have ahold of a valid Integer in the first place. While others might steer you towards String#to_i, a more robust validation is to use Kernel#Integer so that an exception will be raised if the value can't be coerced into a valid Integer. For example:
print "Enter integer: "
percentage = Integer gets
If you enter 15\n then:
percentage.class
#=> Integer
If you enter something that can't be coerced to an Integer, like foo\n, then:
ArgumentError (invalid value for Integer(): "foo\n")
Using String#to_i is much more permissive, and can return 0 when you aren't expecting it, such as when called on nil, an empty string, or alphanumeric values that don't start with an integer. It has other interesting edge cases as well, so it's not always the best option for validating input.
I'm trying to find the amount from a percentage that a user inputs
If you retrieve the input via gets, you typically convert it to a numeric value first, e.g.
percentage = gets.to_i
#=> 15
Ruby is not aware that this 15 is a percentage. And since there's no Percentage class, you have to convert it into one of the existing numeric classes.
15% is equal to the fraction 15/100, the ratio 15:100, or the decimal number 0.15.
If you want the number as a (maybe inexact) Float, you can divide it by 100 via fdiv:
15.fdiv(100)
#=> 0.15
If you prefer a Rational you can use quo: (it might also return an Integer)
15.quo(100)
#=> (3/20)
Or maybe BigDecimal for an arbitrary-precision decimal number:
require 'bigdecimal'
BigDecimal(15) / 100
#=> 0.15e0
BigDecimal also accepts strings, so you could pass the input without prior conversion:
input = gets
BigDecimal(input) / 100
#=> 0.15e0

Does Oracle's ADO.NET data provider report wrong data types for NUMBER(x,y) and FLOAT or REAL?

I suspect that Oracle's data provider for ADO.NET reports wrong data types for columns whose type is NUMBER(x,y), FLOAT or REAL.
The following simple program creates a table with these data types and then prints the data types as reported by the data provider. When executed, it prints:
NUM: Double - System.Double - Double
FLT: Decimal - System.Decimal - Decimal
REL: Decimal - System.Decimal - Decimal
However, I feel, it should be the other way round and get a DECIMAL type for FLT and REL and a Double tyep for NUM.
Can someone confirm my suspicion?
using System;
using Oracle.DataAccess.Client;
class Prg {
static void Main() {
OracleConnection ora = new OracleConnection($"user Id=rene;password=rene;data source=ORA18");
ora.Open();
OracleCommand stmt = ora.CreateCommand();
stmt.CommandText = "begin execute immediate 'drop table DataTypeTest'; exception when others then null; end;";
stmt.ExecuteNonQuery();
stmt.CommandText = "create table DataTypeTest (num number(10,3), flt float, rel real)";
stmt.ExecuteNonQuery();
stmt.CommandText = "select * from DataTypeTest";
OracleDataReader res = stmt.ExecuteReader();
for (int fld=0; fld<res.FieldCount; fld++) {
Console.WriteLine($"{res.GetName(fld)}: {res.GetDataTypeName(fld)} - {res.GetFieldType(fld)} - {Type.GetTypeCode(res.GetFieldType(fld))}");
}
}
}
From Oracle Datatypes Documentation:
FLOAT [(p)]
A subtype of the NUMBER datatype having precision p. A FLOAT value is represented internally as NUMBER. The precision p can range from 1 to 126 binary digits. A FLOAT value requires from 1 to 22 bytes.
And further down that documentation page:
ANSI SQL Datatype: REAL (Note d)
Oracle Datatype: FLOAT(63)
Notes 1d: The REAL datatype is a floating-point number with a binary precision of 63, or 18 decimal.
So REAL is a sub-type of FLOAT which, in turn, is a sub-type of NUMBER and that NUMBER should allow the greatest precision and REAL the least precision of these data types.
I suspect that Oracle's data provider for ADO.NET reports wrong data types for columns whose type is NUMBER(x,y), FLOAT or REAL.
Looking at:
ODBC Data Type Mappings:
ODBC type .NET Framework type
----------- -------------------
SQL_REAL Single
SQL_NUMERIC Decimal
SQL_DOUBLE Double
(SQL_FLOAT was not listed in the table and may default to SQL_NUMERIC's mapping since one is a sub-type of the other.)
It does appear that your test data does not match with these mappings.
However:
Since both REAL and FLOAT are sub-types of NUMERIC in the Oracle database then if they are being reported as their NUMERIC super-type rather than their specific sub-type then the mapping to Decimal does match the type mapping.
Your NUM column is of type NUMBER(10,3) so can have at most 7 whole digits and 3 decimal places and it may be that the data provider has determined that this can be accurately stored in a Double data type and that a Decimal data type is overkill. You can see if a different data type is returned using NUMBER (without precision or scale) or NUMBER(38,3).

Custom data format in OBIEE - Showing decimal when double, no decimal when integer

I have measure column, which I am using for pivoting, and I have also used New calculated items. Now the new calculated item is to return data in double format,which is percentage, but the other results is to return data in integer. If the data format of the column is decimal then the measure column, which are integers would show data with decimals (so 2 becomes 2.00), and if i keep it integer then decimals from the percentage column would be removed (so 45.28% becomes 45%).
Can the data format of the column be changed such that when there are decimal, then decimals are returned and when whole numbers, whole numbers are returned (without the .00s)?
Expected Result
A B (A/B)*100
2 6 33.33
Note that A and B are coming from the same column, and the (A/B)*100 is my New Calculated Item.
Criteria tab / Properties / Data format and select "up to 2" in decimal places. This option excludes the ".00" for integer values.

float, round to 2 decimal places - Processing

I started learning processing since a short time ago and I came across a problem; When deviding 199.999 by 200 I want to outcome to be with 2 decimals (so the outcome should be 1 rounded of). Without formatting the outcome is 0.999995.
Code for formatting to String with 2 decimal:
float money = 199.999;
int munten = 200;
String calc1 = nf(money/munten,0,2);
println(calc1);
float calc2 = float(calc1);
println(calc2);
Prints:
1,0
NaN
I think float() wont work cause there is a comma in the String instead of a dot, I'm not sure tough. But how can I round a number to 2 decimal and still let it be a float?
Thanks for taking your time to read this,
When I run your example on Processing 3.3.6 / macOS 10.12 (US), I get "1.00" and "1.0". This could be due to your number formatting settings creating output strings that are then not read correctly by nf().
float money;
int munten;
String s;
float f;
money = 199.999;
munten = 200;
s = nf(money/munten, 0, 2);
println(s); // "1.00" -- or "1,0" etc. in different os language locales
f = float(s);
println(f); // "1.0" -- or NaN error if above is not 1.0 format
f = money/munten;
println(f); // 0.999995
s = nf(f, 0, 2);
println(s); // 1.00 -- or local format
You can see what should be happening more clearly in the second bit of code -- don't try to convert into a String and then back out again; don't store numbers in Strings. Instead, keep everything in numeric variables up until the moment you need to display.
Also keep in mind that nf() isn't really for rounding precision, although it is often used that way:
nf() is used to add zeros to the left and/or right of a number. This is typically for aligning a list of numbers. To remove digits from a floating-point number, use the int(), ceil(), floor(), or round() functions. https://processing.org/reference/nf_.html
If you need to work around your locale, you can use Java String formatting in Processing to do so:
float fval = 199.999/200;
println(fval); // 0.999995
String s = String.format(java.util.Locale.US,"%.2f", fval);
println(s); // 1.00
See https://stackoverflow.com/a/5383201/7207622 for more discussion of the Java approach.

How do I format a currency with commas and 2 decimal places?

I am trying to format some numbers as a currency, with commas and 2 decimal places. I've found "github.com/dustin/go-humanize" for the commas but it doesn't allow for specifying the number of decimal places. fmt.Sprintf will do the currency and decimal formatting but not the commas.
for _, fl := range []float64{123456.789, 123456.0, 123456.0100} {
log.Println(humanize.Commaf(fl))
}
Results:
123,456.789
123,456
123,456.01
I am expecting:
$123,456.79
$123,456.00
$123,456.01
That would be what the humanize.FormatFloat() does:
// FormatFloat produces a formatted number as string based on the following user-specified criteria:
// * thousands separator
// * decimal separator
// * decimal precision
In your case:
FormatFloat("$#,###.##", afloat)
That being said, as commented by LenW, float (in Go, float64) is not a good fit for currency.
See floating-point-gui.de.
Using a package like go-inf/inf (previously go/dec, used for instance in this currency implementation) is better.
See Dec.go:
// A Dec represents a signed arbitrary-precision decimal.
// It is a combination of a sign, an arbitrary-precision integer coefficient
// value, and a signed fixed-precision exponent value.
// The sign and the coefficient value are handled together as a signed value
// and referred to as the unscaled value.
That type Dec does include a Format() method.
Since July 2015, you now have leekchan/accounting from Kyoung-chan Lee (leekchan) with the same advice:
Please do not use float64 to count money. Floats can have errors when you perform operations on them.
Using big.Rat (< Go 1.5) or big.Float (>= Go 1.5) is highly recommended. (accounting supports float64, but it is just for convenience.)
fmt.Println(ac.FormatMoneyBigFloat(big.NewFloat(123456789.213123))) // "$123,456,789.21"
There is a good blog post about why you should never use floats to represent currency here - http://engineering.shopspring.com/2015/03/03/decimal/
From their examples you can :
d := New(-12345, -3)
println(d.String())
Will give you :
-12.345
fmt.Printf("%.2f", 12.3456)
-- output is 12.34

Resources