I have a select statement which return capacity as exponential value e.g.
Capacity=5.4835615662E+003
in Perl code
I am using a db2 database, and if I explicitly run a query in database it returns
5483.5615662
but when I use next select query when I use capacity value in condition it doesn't match
e.g. pseudo code is as below,
my $capacity = 'SELECT capacity FROM table';
# it returns $capacity = 5.4835615662E+003
my $result = "SELECT MEASUREMENT FROM TABLE WHERE CAPACITY = $capacity";
Here $capacity is 5.4835615662E+003, so it does not match any row in the table. It should be 5483.5615662.
How to convert exponential value to float without rounding off?
You are interpolating the value of $capacity into a string. Instead, you should use placeholders as in:
my $sth = $dbh->prepare(q{SELECT MEASUREMENT FROM TABLE WHERE CAPACITY=?});
$sth->execute($capacity);
It is hard to say if there are any other problems because the code snippets you provide don't really do anything.
It is likely that the number stored in the database is not exactly 5483.5615662 and that is just the displayed string when you query it.
If possible, I would recommend taking #Сухой27's advice and letting the database do the work for you:
SELECT MEASUREMENT FROM TABLE
WHERE CAPACITY = (SELECT CAPACITY FROM TABLE where ..?)
Alternatively, decide ahead of time how many digits past the decimal point really matter and use ROUND or similar functionality:
my $sth = $dbh->prepare(q{
SELECT MEASUREMENT FROM TABLE
WHERE ROUND(CAPACITY, 6)=ROUND(?, 6)
});
$sth->execute($capacity);
Please take a look at Why doesn't this sql query return any results comparing floating point numbers?
I'm concerned about the 5.4835615662+003 that you show in your question. That isn't a valid representation of a number, and it means just 5.4835615662 + 3. You need an E or an e before the exponent to use it as it is
There is also an issue with comparing floating-point values, whereby two numbers that are essentially equal may have a slightly different binary representation, and so will not compare as equal. If your value has been converted to a string (and that seems highly likely, as Perl will not use an exponent to display 5483.5615662 unless told to do so) and back again to floating point, then it is extremely unlikely to result in exactly the same value. Your comparisons will always fail
In Perl, and most other languages, a numeric values has no specific format. For example, if I run this
perl -E 'say 5.4835615662E+003'
I get the output
5483.5615662
showing that the two string representations are equivalent
It would help to see exactly how you got the value of $capacity from the database, because if it were a simple number then it wouldn't use the scientific representation. You would have to use sprintf to get what you have shown
SQL is the same and doesn't care about the format of the number as long as it's valid, so if you wrote
SELECT measurement FROM table WHERE capacity = 5.4835615662E+003
then you would get a result where capacity is exactly equal to that value. But since it has been trimmed to eleven significant digits, you are hugely unlikely to find the record that the value came from, unless it contains 5483.56156620000000000
Update
If I run
perl -MMath::Trig=pi -E 'for (0 .. 20) { $x = pi * 10**$_; say qq{$x}; }'
I get this result
3.14159265358979
31.4159265358979
314.159265358979
3141.59265358979
31415.9265358979
314159.265358979
3141592.65358979
31415926.5358979
314159265.358979
3141592653.58979
31415926535.8979
314159265358.979
3141592653589.79
31415926535897.9
314159265358979
3.14159265358979e+015
3.14159265358979e+016
3.14159265358979e+017
3.14159265358979e+018
3.14159265358979e+019
3.14159265358979e+020
So by default Perl won't resort to using scientific notation until the value reaches 1015. It clearly doesn't apply to 5483.5615662. Something has coerced the floating-point value in the question to a much less precise string in scientific notation. Comparing that for equality doesn't stand a chance of succeeding
Before I get flamed, I want to say I do understand floating point numbers and things of the sort, but that doesn't seem to be my issue.
To simplify things, I'm trying to determine if a number has more than 2 decimal places. I'm doing this by multiplying the number by 100 (stored under variable "test1") and then truncating it with int() ($test2) and comparing it with an if.
$test1 = $number * 100;
$test2 = int($test1);
unless ($test1 == $test2) {
die ("test1:$test1, test2:$test2");
}
The initial $number comes from a whole series of other functions and should realistically be only two decimals, hence I'm trying to catch those that aren't (as a few entries seem to have very many decimals).
However, I just got:
test1:15, test2:14
from my die().
Can someone explain how that would happen? How can int(15) be 14?
From perldoc:
machine representations of floating-point numbers can sometimes produce counterintuitive results. For example, int(-6.725/0.025) produces -268 rather than the correct -269; that's because it's really more like -268.99999999999994315658 instead
So, the machine representation of "15" is probably something like 14.9999999999999999 and, therefore, int truncates it to 14.
Note that perldoc suggests using the POSIX functions floor or ceil instead.
In a simple, one off, case adding 0.5 to your value before int-ing it will give you what you want.
e.g.
int(14.99 + 0.5)
15
it becomes 15.49 and is int-ed "down" to 15, whereas:
int( 14.45 + 0.5 )
still gets int'ed "down" to 14.0. This is a handy trick but doesn't self document as nicely as using floor and ceil.
As a side note, the Goldberg paper on floating point arithmetic always reminds me how useful it sometimes is to have brains that are not as mindlessly precise as a computer :-)
If I wanted to check if a number had more than two decimal places, I wouldn't do math on it.
my $more_than_two = $number =~ /\d+\.\d{2}\d+\z/;
Before I do that, I might use Scalar::Util's looks_like_a_number. This method will still fail with floating point squishiness if you were expecting 14.99999 to be 15.0.
However, you should tell us what you are trying to do instead of how you are trying to do that. It's easier to give better answers.
For your questions about int, I think it's documentation tell you what you need to know. The rest is answered in the first couple of questions in perlfaq4.
I am trying to find out if an input is number or string. I came across looks_like_number and cannot understand the values it returns.
use warnings;
use Scalar::Util qw(looks_like_number);
my $name = 11;
print looks_like_number ($name);
This code prints 1 if $name contains a string and a static number if $name contains an integer (i.e. 4352 for each integer).
I am using Perl on Windows.
You forgot to ask a question! Here are two possibilities.
Why doesn't it always return the same value for true?
Why not? It returns a true value as documented. It makes no difference which true value it is.
What is the value returned?
If the scalar contains a string, it uses grok_number which has specific document return values.
The type of the number is returned (0 if unrecognised), otherwise it is a bit-ORed combination of IS_NUMBER_IN_UV, IS_NUMBER_GREATER_THAN_UV_MAX, IS_NUMBER_NOT_INT, IS_NUMBER_NEG, IS_NUMBER_INFINITY, IS_NUMBER_NAN (defined in perl.h).
Otherwise, it uses
SvFLAGS(sv) & (SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK)
You can't tell which of the two was used, so you can't ascribe meaning to the value, which is why it's undocumented.
Don't rely on the exact numerical value. This is an abstraction leak, which the latest version of Scalar::Util (1.39) fixes. What is important is simply the truth of the result, not its exact numerical value.
See bug https://rt.cpan.org/Ticket/Display.html?id=94806
This is what the documentation says:
looks_like_number EXPR
Returns true if perl thinks EXPR is a number. See "looks_like_number" in perlapi.
The link to perlapi in this quote is not really helping us a lot unfortunately:
Test if the content of an SV looks like a number (or is a number). Inf
and Infinity are treated as numbers (so will not issue a non-numeric
warning), even if your atof() doesn't grok them. Get-magic is ignored.
I32 looks_like_number(SV *const sv)
In my case, your code will return an integer that is not 0, which is true.
I got 4352 when I used 11.
When I used '11' I got 1.
All of these are true, so that works.
When I put 'test' or 'foobar' I got 0, which is not true.
I never got a 1 for anything that did not look like a number.
I tried '1e1' and it printed 4, which is a true value, and the input looked like a number in scientific notation.
So, I'd say it always returns something true if Perl thinks the input looks like a number, though I do not know what exactly that true value represents. I cannot confirm that it also returns true with a name.
So, I happened to notice that last.fm is hiring in my area, and since I've known a few people who worked there, I though of applying.
But I thought I'd better take a look at the current staff first.
Everyone on that page has a cute/clever/dumb strapline, like "Is life not a thousand times too short for us to bore ourselves?". In fact, it was quite amusing, until I got to this:
perl -e'print+pack+q,c*,,map$.+=$_,74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21, 18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34'
Which I couldn't resist pasting into my terminal (kind of a stupid thing to do, maybe), but it printed:
Just another Last.fm hacker,
I thought it would be relatively easy to figure out how that Perl one-liner works. But I couldn't really make sense of the documentation, and I don't know Perl, so I wasn't even sure I was reading the relevant documentation.
So I tried modifying the numbers, which got me nowhere. So I decided it was genuinely interesting and worth figuring out.
So, 'how does it work' being a bit vague, my question is mainly,
What are those numbers? Why are there negative numbers and positive numbers, and does the negativity or positivity matter?
What does the combination of operators +=$_ do?
What's pack+q,c*,, doing?
This is a variant on “Just another Perl hacker”, a Perl meme. As JAPHs go, this one is relatively tame.
The first thing you need to do is figure out how to parse the perl program. It lacks parentheses around function calls and uses the + and quote-like operators in interesting ways. The original program is this:
print+pack+q,c*,,map$.+=$_,74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21, 18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34
pack is a function, whereas print and map are list operators. Either way, a function or non-nullary operator name immediately followed by a plus sign can't be using + as a binary operator, so both + signs at the beginning are unary operators. This oddity is described in the manual.
If we add parentheses, use the block syntax for map, and add a bit of whitespace, we get:
print(+pack(+q,c*,,
map{$.+=$_} (74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21,
18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34)))
The next tricky bit is that q here is the q quote-like operator. It's more commonly written with single quotes:
print(+pack(+'c*',
map{$.+=$_} (74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21,
18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34)))
Remember that the unary plus is a no-op (apart from forcing a scalar context), so things should now be looking more familiar. This is a call to the pack function, with a format of c*, meaning “any number of characters, specified by their number in the current character set”. An alternate way to write this is
print(join("", map {chr($.+=$_)} (74, …, -34)))
The map function applies the supplied block to the elements of the argument list in order. For each element, $_ is set to the element value, and the result of the map call is the list of values returned by executing the block on the successive elements. A longer way to write this program would be
#list_accumulator = ();
for $n in (74, …, -34) {
$. += $n;
push #list_accumulator, chr($.)
}
print(join("", #list_accumulator))
The $. variable contains a running total of the numbers. The numbers are chosen so that the running total is the ASCII codes of the characters the author wants to print: 74=J, 74+43=117=u, 74+43-2=115=s, etc. They are negative or positive depending on whether each character is before or after the previous one in ASCII order.
For your next task, explain this JAPH (produced by EyesDrop).
''=~('(?{'.('-)#.)#_*([]#!#/)(#)#-#),#(##+#)'
^'][)#]`}`]()`#.#]#%[`}%[#`#!##%[').',"})')
Don't use any of this in production code.
The basic idea behind this is quite simple. You have an array containing the ASCII values of the characters. To make things a little bit more complicated you don't use absolute values, but relative ones except for the first one. So the idea is to add the specific value to the previous one, for example:
74 -> J
74 + 43 -> u
74 + 42 + (-2 ) -> s
Even though $. is a special variable in Perl it does not mean anything special in this case. It is just used to save the previous value and add the current element:
map($.+=$_, ARRAY)
Basically it means add the current list element ($_) to the variable $.. This will return a new array with the correct ASCII values for the new sentence.
The q function in Perl is used for single quoted, literal strings. E.g. you can use something like
q/Literal $1 String/
q!Another literal String!
q,Third literal string,
This means that pack+q,c*,, is basically pack 'c*', ARRAY. The c* modifier in pack interprets the value as characters. For example, it will use the value and interpret it as a character.
It basically boils down to this:
#!/usr/bin/perl
use strict;
use warnings;
my $prev_value = 0;
my #relative = (74,43,-2,1,-84, 65,13,1,5,-12,-3, 13,-82,44,21, 18,1,-70,56, 7,-77,72,-7,2, 8,-6,13,-70,-34);
my #absolute = map($prev_value += $_, #relative);
print pack("c*", #absolute);
I am just learning Perl's comparison operators. I tried the below code :-
$foo=291;
$bar=30;
if ($foo < $bar) {
print "$foo is less than $bar (first)\n";
}
if ($foo lt $bar) {
print "$foo is less than $bar (second)\n";
}
The output is 291 is less than 30 (second). Does this mean the lt operator always converts the variables to string and then compare? What is the rationale for Perl making lt operator behave differently from the < operator?
Thanks,
Your guess is right. The alphabetic operators like lt compare the variables as strings whereas the symbolic ones like < compare them as numbers. You can read the perlop man page for more details.
The rationale is that scalars in Perl are not typed, so without you telling it Perl would not know how to compare two variables. If it did guess then it would sometimes getting it wrong, which would lead to having to do things like ' ' + $a < ' ' + $b to force string comparsion which is probably worse than lt.
That said this is a horrible gotcha which probably catches out everyone new to Perl and still catches me out when coming back to Perl after some time using a less post-modern language.
Since Perl is loosely typed, and values can silently convert between strings and integers at any moment, Perl needs two different types of comparison operators to distinguish between integer comparison (<) and string comparison (lt). If you only had one operator, how would you tell the difference?
Rationale? It's a string operator. From "perldoc perlop":
Binary "lt" returns true if the left argument is stringwise less than the right argument.
If that's not what you want, don't use it.
lt compares values lexically (i.e. in ASCII/UNICODE or locale order) and < compares values numerically. Perl has both operators for the same reason "10" + 5 is 15 rather than a type error: it is weakly typed. You must always tell the computer something unambiguous. Languages that are strongly typed tend to use casting to resolve ambiguity, whereas, weakly typed languages tend to use lots of operators. The Python (a strongly typed language) equivalent to "10" + 5 is float("10") + 5.
Does this mean the 'lt' operator
always converts the variables to
string and then compare?
Yes, see perlop
What is the rationale for Perl making
'lt' operator behave differently from
'<' operator?
Because having a numeric comparison operator and a string comparison operator makes a lot more sense then having a mumble mumble operator and another, identical mumble mumble operator.