ColdFusion's LSCurrencyFormat function appears to be using round-to-half-even (banker's rounding). Is there anyway to change this? I'd like to change it to standard round-half-up rounding that most people are taught in grade school.
An example:
LSCurrencyFormat(39.7340): $39.73
LSCurrencyFormat(39.7350): $39.74
LSCurrencyFormat(39.7360): $39.74
LSCurrencyFormat(39.7440): $39.74
LSCurrencyFormat(39.7450): $39.74 <== I want this to be $39.75
LSCurrencyFormat(39.7460): $39.75
I do not think there is a way to customize the rounding mode used by the numeric functions. (Though I could be wrong) You may have to dip into java for customized rounding behavior
Update My mistake. I thought the need was for something basic masks did not already provide. Oh, well. Maybe this example will be useful to someone anyway ..
Update Added HALF_UP rounding mode example
(Note: The Locale handling is quick and dirty. I am sure there is a more elegant way of doing it..)
<cfset Locale = createObject("java", "java.util.Locale")>
<cfset Mode = createObject("java", "java.math.RoundingMode")>
<cfset Formatter = createObject("java", "java.text.NumberFormat").getCurrencyInstance(Locale.US)>
<cfset Formatter.applyPattern("$######,######.####")>
<cfset input = LSParseNumber("39.735", "en_US")>
Input <cfoutput>#input#<br></cfoutput>
<cfset Formatter.setRoundingMode( Mode.HALF_EVEN )>
HALF_EVEN <cfoutput>#Formatter.format(input)#<br></cfoutput>
<cfset Formatter.setRoundingMode( Mode.HALF_DOWN )>
HALF_DOWN <cfoutput>#Formatter.format(input)#<br></cfoutput>
<cfset Formatter.setRoundingMode( Mode.HALF_UP )>
HALF_UP <cfoutput>#Formatter.format(input)#<br></cfoutput>
Try LSNumberFormat() with a mask
#LSNumberFormat(39.7350,"$_.__")# = $39.7350
Why not round before you send the value to the LSCurrencyFormat?
LSCurrencyFormat(yourRoundingFunction(39.7450))
Also see http://jamiekrug.com/blog/index.cfm/2009/2/12/ColdFusion-round-function-bug-when-operation-performed-on-argument
Related
I'm trying to modify existing codes in my ColdFusion application left by previous programmer. I don't understand the meaning of this line of code (the one with question marks):
<cfset Application[#form.username#] = 0> ??????
<cfset Session.loggedin="Yes">
<cfset Session.username="#Trim(Form.username)#">
Maybe I haven't been working with CF long enough to see this syntax so I don't know what this mean.
When setting an application variable I usually use this syntax:
<cfset application.variableName = "some value">
Can someone explain to me what is this ?
Thank you
As well as explicitly stating variable names at "code time" with dot notation, CFML allows one to reference them dynamically at runtime via a string value.
This is done via associative array notation (using square brackets), eg:
myVariableName = "foo";
variables[myVariableName] = "moo"; // equivalent to variables.foo = "moo"
I have this working nicely in tag format, but am trying to migrate everything into cfscript. How can I do this one? (essentially, it loops over from date1 to date2, and needs to be in 15 minute intervals.
<cfset from=now()>
<cfset to=dateadd("d", 1, from)>
<cfloop from="#from#" to="#to#" index="i" step="#CreateTimeSpan(0,0,15,0)#">
...stuff...
<cfloop>
It's how to specify the step bit which is getting me..
#Jarede's answer certainly gives you a loop which performs the same iterations with the same values as your requirement, but it's not really equivalent to the tag version. This is the closest to your example:
from = now();
to = dateadd("d", 1, from);
step = CreateTimeSpan(0,0,15,0);
for (i=from; i <= to; i+=step){
// ... stuff ...
}
If you're incrementing (or decrementing) and index value, use a for() loop, If your condition is not based around an index value, use a do or a while loop.
As I mentioned in a comment above, if you are not familiar with CFScript, you need to be. I recommend reading this, thoroughly: CFScript. It's the only complete documentation of CFScript I'm aware of. If you notice any omissions... send me a pull request.
<cfscript>
variables.dtNow = now();
variables.dtTmrw = dateAdd('d',1,variables.dtNow);
do {
variables.dtNow = dateAdd('n',15,variables.dtNow);
writeOutput(variables.dtNow);
} while(variables.dtNow neq variables.dtTmrw);
</cfscript>
I've just experienced a behaviour that defies any logic and could potentially lead to serious issues and was wondering if it was a bug or if the behaviour was itended and what are the best practices to circumvent the issue? If it's a bug, is there a patch?
Here's the two wierd behaviours that when put together are a threat to any system's data integrity.
int('1 2') -> 41276
isValid('numeric', '1 2') -> true
Why? Well let's see...
<cffunction name="deleteSomething" access="public" returntype="void">
<cfargument name="somethingId" type="numeric" required="yes">
<cfquery datasource="#dsn()#">
DELETE
FROM Something
WHERE id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.somethingId#">;
</cfquery>
</cffunction>
<cfset deleteSomething('1 2')>
Here, the type="numeric" arguments validation (which perhaps is based on the same algorithm as isValid?) doesn't throw with '1 2'. Even worse, cfqueryparam cfsqltype="cf_sql_integer" seems to be using int to convert the value which will end up being 41276.
In other words, deleteSomething('1 2') will delete the entity with id 41276 instead of throwing an exception since the value 1 2 is obviously not numeric.
Now, the only fix I thought of is to perform additionnal argument validation using isValid('integer', ... or a regular expression, but that's a real pain and besides, I never understood why they haven't implemented type="integer"?
Obviously, I also always made the false assumption that cfqueryparam type="cf_sql_integer" would validate that the value passed is a valid integer.
EDIT:
It seems that even isvalid('integer', ... is also not reliable as we can see in
Why isvalid("integer","1,5") = YES?
EDIT2:
I know that I could add additionnal arguments validation for every expected integer argument in every function, however that would require to fix a huge code base in my case and it's also very error-prone. It also makes the built-in argument validation completely useless in this case.
I would rather prefer a solution where I could create and apply an unofficial patch. Is that a realistic option? If so I would like to be pointed out in the right direction.
EDIT3: It doesn't solves all the problems, but CF11 added support for a strictNumberValidation application level configuration.
"Starting from ColdFusion 11, this function evaluates on a more strict
basis. Setting this value to false makes the isValid function to
behave in the older way. This setting effects cfargument, cfparam and
cfform tags wherever integer & numeric validation is used. Based on
this setting, the validation reflects in those tags as well."
This is a variation on that theme from the other question. See this code (or run it on cflive.net):
<cfscript>
s = "1 2";
i = int(s);
v = isValid("numeric", s);
d = createOdbcDate(s);
writeDump([s,i,v,d]);
</cfscript>
s converts to 41276 when calling int(), and when using it as an input for createOdbcDate(), we get:
January, 02 2013 00:00:00 +0000
So "1 2" is being interpreted as "m d", with an implied year of the current year.
Which is utterly stupid. But there you go.
You can use regular expressions to find out if there are any non numeric characters in a given form field:
reFind( "[^\d-]", "1 2")
That will match any character that is not a number, not a -
If you want to check only positive numbers, you can use
reFind( "[^\d]", "1 2")
If this returns true, you do not have an integer.
Got a critical error like: The value cannot be converted to a number. what can be the problem? Since i tried to write this values like:
1. <cfset ortalama=trim(val(db_maliyet_temp))+ds_toplam_maliyet>
2. <cfset ortalama=val(db_maliyet_temp)+ds_toplam_maliyet>
3. <cfset ortalama=db_maliyet_temp+ds_toplam_maliyet>
the first and second are just doesnt count the db_maliyet_temp,
and the 3 give out the error: The value cannot be converted to a number.
value for db_maliyet_temp: 2.806,71
for ds_toplam_maliyet: 394,22
These are not valid numbers. If you would like the total of the numbers, you can try this.
<cfset aryMaliyetNumbers = ListToArray(db_maliyet_temp, ",")>
<cfset aryToplamNumbers = ListToArray(ds_toplam_maliyet, ",")>
<cfset total = ArraySum(aryMaliyetNumbers) + ArraySum(aryToplamNumbers)>
There are several ways to skin this cat. This should at least get you going. Works perfectly on my CF 7 box!
EDIT
After the ridiculous amount of comments to clarify the question, I believe this is the solution.
<cfset db_maliyet_temp = Replace(Replace("2.806,71", ".", ""), ",", ".")>
<cfset ds_toplam_maliyet = Replace(Replace("394,22", ".", ""), ",", ".")>
<cfset total = db_maliyet_temp + ds_toplam_maliyet>
If you want the number without decimals, you can do this:
<cfset db_maliyet_temp = Replace(Replace("2.806,71", ".", ""), ",", ".")>
<cfset ds_toplam_maliyet = Replace(Replace("394,22", ".", ""), ",", ".")>
<cfset total = val(db_maliyet_temp + ds_toplam_maliyet)>
IMPORTANT
You have a much larger problem than a CF error. You need to fix the underlying issue that's causing your number to be formatted incorrectly.
This should do the trick:
<cfscript>
function convertToNumber(num){
return reReplace(reReplace(num,'.','','ALL'),',','.','ALL');
}
</cfscript>
<cfset ortalama=convertToNumber(db_maliyet_temp)+convertToNumber(ds_toplam_maliyet)>
Basically it just removes the '.' since that is formatting not needed for math and replaces the ',' with a decimal so that it can be treated as a number. This will only work if ALL the numbers you are going to be dealing with are formatted this way, if there are any formatted like 1,200.90 then you will have to be a little more fancy.
Have you tried the LSParseNumber() function?:
http://cfquickdocs.com/cf9/#lsparsenumber
Or the val() function?:
http://cfquickdocs.com/cf9/#val
Also, it may be easier to clean the data before it get's entered either with client-side validation (if it's coming from a form) or server-side validation.
Is there a way of writing this logic in a single, elegant line of code?
<cfif ThumbnailWidth EQ 0>
<cfset Width = 75>
<cfelse>
<cfset Width = ThumbnailWidth>
</cfif>
Coldfusion 9:
<!--- Syntax: ((condition) ? trueStatement : falseStatement) --->
<cfset width = ((ThumbnailWidth EQ 0) ? 75 : ThumbnailWidth) />
Coldfusion 8 and below:
<!--- Syntax: IIf(condition, trueStatement, falseStatement) --->
<cfset width = IIf((ThumbnailWidth EQ 0), 75, ThumbnailWidth) />
Some will say that IIf() is to be avoided for performance reasons. In this simple case I'm sure you'll find no difference. Ben Nadel's Blog has more discussion on IIF() performance and the new ternary operator in CF 9.
I find your original elegant enough - tells the story, easy to read. But that's definitely a personal preference. Luckily there's always at least nine ways to do anything in CFML.
You can put that on one line (CFML has no end-of-line requirements):
<cfif ThumbnailWidth EQ 0><cfset Width = 75><cfelse><cfset Width = ThumbnailWidth></cfif>
You can also use IIF() Function - it'll do the trick:
<cfset Width = IIf(ThumbnailWidth EQ 0, 75, ThumbnailWidth)>
This construct is a little odd tho' - is more clear I think. The strength of IIF() is that it can also be used inline (it is a function after all). For example:
<img src="#ImageName#" width="#IIf(ThumbnailWidth EQ 0, 75, ThumbnailWidth)#">
This last form is often used to maintain a clean(er) HTML layout while injecting dynamic code.
Like Neil said, it's fine the way it is. If you really want a single line you could make it a cfscript with a ternary operator, like:
<cfscript>width = (ThumbnailWidth == 0) ? 75 : ThumbnailWidth;</cfscript>
Haven't tested this code, but it should work.
If you are looking for concise code, then you can take it a step further than the other examples, taking advantage of CF's evaluation of non-zero values as true:
<!--- CF 9 example --->
<cfset width = ThumbnailWidth ? ThumbnailWidth : 75>
<!--- CF 8 and below --->
<cfset width = iif(ThumbnailWidth, ThumbnailWidth, 0)>
Naturally, you'll sacrifice a little clarity, but that's the tradeoff for more compact code.
I personally prefer something more along the lines of this:
<cfscript>
Width = ThumbnailWidth;
if(NOT Val(Width)) // if the Width is zero, reset it to the default width.
Width = 75;
</cfscript>