PS negative numbers. E.g. Ceil(2.5) = 3, Ceil(-2.5) = -2.
Counterintuitively, to ceil a negative number, you remove the sign, *floor* the number, and restore the sign.
Here is a test script and my current and former functions. Any feedback is welcome.
Code: Select all
;q:: ;test Ceil function
vList := ""
VarSetCapacity(vList, 1000000*2)
;num|precision|answer (intuitive)|answer (mathematical)
vList .= " ;continuation section
(
4.05|0|5
4.005|0|5
4.1|0|5
41|0|41
6.004|2|6.01
6.004|1|6.1
6040|-2|6100
6040|-3|7000
2.22|2|2.22|2.23
2.22000000000000020|2|2.22|2.23
2.2|1|2.2|2.2
2.22|2|2.22|2.23
2.222|3|2.222|2.222
2.2222|4|2.2222|2.2222
2.22222|5|2.22222|2.22222
2.222222|6|2.222222|2.222222
2.2222222|7|2.2222222|2.2222222
-4.05|0|-4
-4.005|0|-4
-4.1|0|-4
-41|0|-41
-6.004|2|-6.00
-6.004|1|-6.0
-6040|-2|-6000
-6040|-3|-6000
-2.22|2|-2.22
-2.22000000000000020|2|-2.22
-2.2|1|-2.2
-2.22|2|-2.22
-2.222|3|-2.222
-2.2222|4|-2.2222
-2.22222|5|-2.22222
-2.222222|6|-2.222222
-2.2222222|7|-2.2222222
)"
vLen := 4
vPfx := ""
vList .= "`n`n"
Loop % 10**vLen
{
vNum := "" vPfx Format("0.{:0" vLen "}", A_Index-1)
vList .= vNum "|" vLen "|" vNum "`n"
}
vPfx := "-"
vList .= "`n`n"
Loop % 10**vLen
{
if (A_Index = 1) ;skip '-0'
continue
vNum := "" vPfx Format("0.{:0" vLen "}", A_Index-1)
vList .= vNum "|" vLen "|" vNum "`n"
}
;MsgBox, % vList
oFunc := Func("JEE_Ceil")
;oFunc := Func("JEE_CeilAlt")
;oFunc := Func("helgef_ceiling")
Loop Parse, vList, % "`n", % "`r"
{
if (A_LoopField = "")
{
;vOutput .= "`r`n"
continue
}
oTemp := StrSplit(A_LoopField, "|")
vNum := oTemp[1]
vDP := oTemp[2]
vAnsI := oTemp[3]
vAnsM := oTemp.HasKey(4) ? oTemp[4] : oTemp[3]
vTemp := ""
vAns1 := %oFunc%("" vNum, vDP)
vResult := ""
("" vAns1 = "" vAnsI) && (vResult .= "I")
("" vAns1 = "" vAnsM) && (vResult .= "M")
(vResult = "") && (vResult := "E")
(vResult = "IM") && (vResult := "")
vTemp .= vResult
vAns2 := %oFunc%(0 + vNum, vDP)
vTemp .= "_"
vResult := ""
("" vAns2 = "" vAnsI) && (vResult .= "I")
("" vAns2 = "" vAnsM) && (vResult .= "M")
(vResult = "") && (vResult := "E")
(vResult = "IM") && (vResult := "")
vTemp .= vResult
;MsgBox, % A_LoopField " " vTemp
if !(vTemp = "_")
vOutput .= A_LoopField " " vTemp " " vAns1 "_" vAns2 "`r`n"
}
Clipboard := vOutput
MsgBox, % "done"
return
;==================================================
;e.g. vDP: 2, round up to 2 decimal places
;e.g. vDP: -2, round up to nearest 100
;note: if vDP < 15, rounds to 15dp before ceiling
;e.g. JEE_Ceil(2.22, 2) = 2.22
;e.g. JEE_Ceil(2.2200000000000002, 2) = 2.22
;even though (2.22 == 2.2200000000000002) = 1
;and so both numbers in principle should ceil to 2.23
;rounding to 15dp occurs to give a more intuitive result
;i.e. by '2.2200000000000002' we probably meant '2.22'
JEE_Ceil(vNum, vDP:=0)
{
local
static vIsV1 := !!SubStr(1, 0)
if !vDP
return Ceil(vNum)
else if (vDP < 0)
return Ceil(Ceil(vNum * (10**vDP)) * (10**-vDP))
else if (vDP >= 15)
;return "" Format("{:0." vDP "f}", Ceil(vNum * (10**vDP)) * (10**-vDP))
return "" Round(Ceil(vNum * (10**vDP)) * (10**-vDP), vDP)
else
{
vIsNeg := (vNum < 0)
;round to 15 decimal places to discard some precision,
;e.g. 2.22000000000000020 becomes 2.22
vNum := "" RTrim(Round(vNum, 15), "0")
vCount := StrLen(vNum) - InStr(vNum, ".")
if (vCount = vDP)
return vNum
else if (vCount < vDP)
return vNum Format("{:0" (vDP-vCount) "}", 0)
;9223372036854775807 max int64 (19-digit number)
vNum := SubStr(vNum, 1, vDP-vCount)
if vIsNeg
return vNum
if (StrLen(vNum) >= 19+1) ;note: +1 for the decimal point
;throw Exception("", -1)
;return "" Format("{:0." vDP "f}", Ceil(vNum * (10**vDP)) * (10**-vDP))
return "" Round(Ceil(vNum * (10**vDP)) * (10**-vDP), vDP)
vNum := StrReplace(vNum, ".")
vNum := Ceil(vNum) + 1
return "" Format("{:01}", SubStr(vNum, 1, -vDP)) "." Format("{:0" vDP "}", SubStr(vNum, vIsV1-vDP)) ;note: use Format to convert blank string to 0
}
}
;==================================================
;e.g. vDP: 2, round up to 2 decimal places
;e.g. vDP: -2, round up to nearest 100
;note: does not round before ceiling
;e.g. JEE_CeilAlt(2.22, 2) = 2.23
;e.g. JEE_CeilAlt(2.2200000000000002, 2) = 2.23
;both give the same result since (2.22 == 2.2200000000000002) = 1
JEE_CeilAlt(vNum, vDP:=0)
{
local
if !vDP
return Ceil(vNum)
else if (vDP > 0)
;return "" Format("{:0." vDP "f}", Ceil(vNum * (10**vDP)) * (10**-vDP))
return "" Round(Ceil(vNum * (10**vDP)) * (10**-vDP), vDP)
else ;if (vDP < 0)
return Ceil(Ceil(vNum * (10**vDP)) * (10**-vDP))
}
;==================================================
[EDIT:] Added a fix re. numbers with leading zeros.
The function treats 2.22000000000000020 and "2.22000000000000020" in the same way, and returns 2.22 for both, when 2dp is specified.
I am also considering writing a function that does all checks/manipulations via strings. I.e. part of a maths via strings library.