How to convert REAL48 float into a double - php
I am connecting to a Pervasive SQL database which splits some data over two fields. DOUBLE fields are actually split into fieldName_1 and fieldName_2 where _1 is a 2 byte int and _2 is a 4 byte int.
I want to take these values and convert them using PHP into a usable value.
I have some example code to do the conversion, but it is written in Delphi which I do not understand:
{ Reconstitutes a SmallInt and LongInt that form }
{ a Real into a double. }
Function EntConvertInts (Const Int2 : SmallInt;
Const Int4 : LongInt) : Double; StdCall;
Var
TheRealArray : Array [1..6] Of Char;
TheReal : Real;
Begin
Move (Int2, TheRealArray[1], 2);
Move (Int4, TheRealArray[3], 4);
Move (TheRealArray[1], TheReal, 6);
Result := TheReal;
End;
Some data [fieldName_1,fieldName_2]
[132, 805306368] -> this should be 11
[132, 1073741824] -> this should be 12
I don't understand the logic enough to be able to port this into PHP. Any help would be most appreciated. Thanks
EDIT.
This is the C code that they provided, showing sign/exponent:
double real_to_double (real r)
/* takes Pascal real, return C double */
{
union doublearray da;
unsigned x;
x = r[0] & 0x00FF; /* Real biased exponent in x */
/* when exponent is 0, value is 0.0 */
if (x == 0)
da.d = 0.0;
else {
da.a[3] = ((x + 894) << 4) | /* adjust exponent bias */
(r[2] & 0x8000) | /* sign bit */
((r[2] & 0x7800) >> 11); /* begin significand */
da.a[2] = (r[2] << 5) | /* continue shifting significand */
(r[1] >> 11);
da.a[1] = (r[1] << 5) |
(r[0] >> 11);
da.a[0] = (r[0] & 0xFF00) << 5; /* mask real's exponent */
}
return da.d;
}
Adding this as another answer because I've finally figured this out. Here is PHP code which will convert the values. It has to be manually calculated because PHP does not know how to unpack a Real48 (non standard). Explanation in comments below.
function BiIntToReal48($f1, $f2){
$x = str_pad(decbin($f1), 16, "0", STR_PAD_LEFT);
$y = str_pad(decbin($f2), 32, "0", STR_PAD_LEFT);
//full Real48 binary string
$real48 = $y . $x;
//Real48 format is V = (-1)^s * 1.f * 2^(exp-129)
// rightmost eight bits are the exponent (bits 40-->47)
// subtract 129 to get the final value
$exp = (bindec(substr($real48, -8)) - 129);
//Sign bit is leftmost bit (bit[0])
$sign =$real48[0];
//Now work through the significand - bits are fractional binary
//(1/2s place, 1/4s place, 1/8ths place, etc)
// bits 1-->39
// significand is always 1.fffffffff... etc so start with 1.0
$sgf = "1.0";
for ($i = 1; $i <= 39; $i++){
if ($real48[$i] == 1){
$sgf = $sgf + pow(2,-$i);
}
}
//final calculation
$final = pow(-1, $sign) * $sgf * pow(2,$exp);
return($final);
}
$field_1 = 132;
$field_2 = 805306368;
$ConvVal = BiIntToReal48($field_1, $field_2);
// ^ gives $ConvVal = 11, qed
I've been working on this issue for about a week now trying to get it sorted out for our organisation.
Our Finance dept use IRIS Exchequer and we need to get costs out. Using the above PHP code, I managed to get it working in Excel VBA with the following code (includes dependent functions). If not properly attributed below, I got all the long dec to bin functions from www.sulprobil.com. If you copy and paste the following code block into a Module you can reference my ExchequerDouble function from a cell.
Before I continue, I have to point out one error in the C/PHP code above. If you look at the Significand loops:
C/PHP: Significand = Significand + 2 ^ (-i)
VBA: Significand = Significand + 2 ^ (1 - i)
I noticed during testing that the answers were very close but often incorrect. Drilling further down I narrowed it down to the Significand. It might be a problem with translating the code from one language/methodology to another, or may have simply been a typo, but adding that (1 - i) made all the difference.
Function ExchequerDouble(Val1 As Integer, Val2 As Long) As Double
Dim Int2 As String
Dim Int4 As String
Dim Real48 As String
Dim Exponent As String
Dim Sign As String
Dim Significand As String
'Convert each value to binary
Int2 = LongDec2Bin(Val1, 16, True)
Int4 = LongDec2Bin(Val2, 32, True)
'Concatenate the binary strings to produce a 48 bit "Real"
Real48 = Int4 & Int2
'Calculate the exponent
Exponent = LongBin2Dec(Right(Real48, 8)) - 129
'Calculate the sign
Sign = Left(Real48, 1)
'Begin calculation of Significand
Significand = "1.0"
For i = 2 To 40
If Mid(Real48, i, 1) = "1" Then
Significand = Significand + 2 ^ (1 - i)
End If
Next i
ExchequerDouble = CDbl(((-1) ^ Sign) * Significand * (2 ^ Exponent))
End Function
Function LongDec2Bin(ByVal sDecimal As String, Optional lBits As Long = 32, Optional blZeroize As Boolean = False) As String
'Transforms decimal number into binary number.
'Reverse("moc.LiborPlus.www") V0.3 P3 16-Jan-2011
Dim sDec As String
Dim sFrac As String
Dim sD As String 'Internal temp variable to represent decimal
Dim sB As String
Dim blNeg As Boolean
Dim i As Long
Dim lPosDec As Long
Dim lLenBinInt As Long
lPosDec = InStr(sDecimal, Application.DecimalSeparator)
If lPosDec > 0 Then
If Left(sDecimal, 1) = "-" Then 'negative fractions later..
LongDec2Bin = CVErr(xlErrValue)
Exit Function
End If
sDec = Left(sDecimal, lPosDec - 1)
sFrac = Right(sDecimal, Len(sDecimal) - lPosDec)
lPosDec = Len(sFrac)
Else
sDec = sDecimal
sFrac = ""
End If
sB = ""
If Left(sDec, 1) = "-" Then
blNeg = True
sD = Right(sDec, Len(sDec) - 1)
Else
blNeg = False
sD = sDec
End If
Do While Len(sD) > 0
Select Case Right(sD, 1)
Case "0", "2", "4", "6", "8"
sB = "0" & sB
Case "1", "3", "5", "7", "9"
sB = "1" & sB
Case Else
LongDec2Bin = CVErr(xlErrValue)
Exit Function
End Select
sD = sbDivBy2(sD, True)
If sD = "0" Then
Exit Do
End If
Loop
If blNeg And sB <> "1" & String(lBits - 1, "0") Then
sB = sbBinNeg(sB, lBits)
End If
'Test whether string representation is in range and correct
'If not, the user has to increase lbits
lLenBinInt = Len(sB)
If lLenBinInt > lBits Then
LongDec2Bin = CVErr(x1ErrNum)
Exit Function
Else
If (Len(sB) = lBits) And (Left(sB, 1) <> -blNeg & "") Then
LongDec2Bin = CVErr(xlErrNum)
Exit Function
End If
End If
If blZeroize Then sB = Right(String(lBits, "0") & sB, lBits)
If lPosDec > 0 And lLenBinInt + 1 < lBits Then
sB = sB & Application.DecimalSeparator
i = 1
Do While i + lLenBinInt < lBits
sFrac = sbDecAdd(sFrac, sFrac) 'Double fractional part
If Len(sFrac) > lPosDec Then
sB = sB & "1"
sFrac = Right(sFrac, lPosDec)
If sFrac = String(lPosDec, "0") Then
Exit Do
End If
Else
sB = sB & "0"
End If
i = i + 1
Loop
LongDec2Bin = sB
Else
LongDec2Bin = sB
End If
End Function
Function LongBin2Dec(sBinary As String, Optional lBits As Long = 32) As String
'Transforms binary number into decimal number.
'Reverse("moc.LiborPlus.www") V0.3 PB 16-Jan-2011
Dim sBin As String
Dim sB As String
Dim sFrac As String
Dim sD As String
Dim sR As String
Dim blNeg As Boolean
Dim i As Long
Dim lPosDec As Long
lPosDec = InStr(sBinary, Application.DecimalSeparator)
If lPosDec > 0 Then
If (Left(sBinary, 1) = "1") And Len(sBin) >= lBits Then 'negative fractions later..
LongBin2Dec = CVErr(xlErrVa1ue)
Exit Function
End If
sBin = Left(sBinary, lPosDec - 1)
sFrac = Right(sBinary, Len(sBinary) - lPosDec)
lPosDec = Len(sFrac)
Else
sBin = sBinary
sFrac = ""
End If
Select Case Sgn(Len(sBin) - lBits)
Case 1
LongBin2Dec = CVErr(x1ErrNum)
Exit Function
Case 0
If Left(sBin, 1) = "1" Then
sB = sbBinNeg(sBin, lBits)
blNeg = True
Else
sB = sBin
blNeg = False
End If
Case -1
sB = sBin
blNeg = False
End Select
sD = "1"
sR = "0"
For i = Len(sB) To 1 Step -1
Select Case Mid(sB, i, 1)
Case "1"
sR = sbDecAdd(sR, sD)
Case "0"
'Do Nothing
Case Else
LongBin2Dec = CVErr(xlErrNum)
Exit Function
End Select
sD = sbDecAdd(sD, sD) 'Double sd
Next i
If lPosDec > 0 Then 'now the fraction
sD = "0.5"
For i = 1 To lPosDec
If Mid(sFrac, i, 1) = "1" Then
sR = sbDecAdd(sR, sD)
End If
sD = sbDivBy2(sD, False)
Next i
End If
If blNeg Then
LongBin2Dec = "-" & sR
Else
LongBin2Dec = sR
End If
End Function
Function sbDivBy2(sDecimal As String, blInt As Boolean) As String
'Divide sDecimal by two, blInt = TRUE returns integer only
'Reverse("moc.LiborPlus.www") V0.3 PB 16-Jan-2011
Dim i As Long
Dim lPosDec As Long
Dim sDec As String
Dim sD As String
Dim lCarry As Long
If Not blInt Then
lPosDec = InStr(sDecimal, Application.DecimalSeparator)
If lPosDec > 0 Then
'Without decimal point lPosDec already defines location of decimal point
sDec = Left(sDecimal, lPosDec - 1) & Right(sDecimal, Len(sDecimal) - lPosDec)
Else
sDec = sDecimal
lPosDec = Len(sDec) + 1 'Location of decimal point
End If
If ((1 * Right(sDec, 1)) Mod 2) = 1 Then
sDec = sDec & "0" 'Append zero so that integer algorithm calculates division exactly
End If
Else
sDec = sDecimal
End If
lCarry = 0
For i = 1 To Len(sDec)
sD = sD & Int((lCarry * 10 + Mid(sDec, i, 1)) / 2)
lCarry = (lCarry * 10 + Mid(sDec, i, 1)) Mod 2
Next i
If Not blInt Then
If Right(sD, Len(sD) - lPosDec + 1) <> String(Len(sD) - lPosDec + 1, "0") Then
'frac part Is non - zero
i = Len(sD)
Do While Mid(sD, i, 1) = "0"
i = i - 1 'Skip trailing zeros
Loop
'Insert decimal point again
sD = Left(sD, lPosDec - 1) _
& Application.DecimalSeparator & Mid(sD, lPosDec, i - lPosDec + 1)
End If
End If
i = 1
Do While i < Len(sD)
If Mid(sD, i, 1) = "0" Then
i = i + 1
Else
Exit Do
End If
Loop
If Mid(sD, i, 1) = Application.DecimalSeparator Then
i = i - 1
End If
sbDivBy2 = Right(sD, Len(sD) - i + 1)
End Function
Function sbBinNeg(sBin As String, Optional lBits As Long = 32) As String
'Negate sBin: take the 2's-complement, then add one
'Reverse("moc.LiborPlus.www") V0.3 PB 16-Jan-2011
Dim i As Long
Dim sB As String
If Len(sBin) > lBits Or sBin = "1" & String(lBits - 1, "0") Then
sbBinNeg = CVErr(xlErrValue)
Exit Function
End If
'Calculate 2 's-complement
For i = Len(sBin) To 1 Step -1
Select Case Mid(sBin, i, 1)
Case "1"
sB = "0" & sB
Case "0"
sB = "1" & sB
Case Else
sbBinNeg = CVErr(xlErrValue)
Exit Function
End Select
Next i
sB = String(lBits - Len(sBin), "1") & sB
'Now add 1
i = lBits
Do While i > 0
If Mid(sB, i, 1) = "1" Then
Mid(sB, i, 1) = "0"
i = i - 1
Else
Mid(sB, i, 1) = "1"
i = 0
End If
Loop
'Finally strip leading zeros
i = InStr(sB, "1")
If i = 0 Then
sbBinNeg = "0"
Else
sbBinNeg = Right(sB, Len(sB) - i + 1)
End If
End Function
Function sbDecAdd(sOne As String, sTwo As String) As String
'Sum up two string decimals.
'Reverse("moc.LiborPlus.www") V0.3 PB 16-Jan-2011
Dim lStrLen As Long
Dim s1 As String
Dim s2 As String
Dim sA As String
Dim sB As String
Dim sR As String
Dim d As Long
Dim lCarry As Long
Dim lPosDec1 As Long
Dim lPosDec2 As Long
Dim sF1 As String
Dim sF2 As String
lPosDec1 = InStr(sOne, Application.DecimalSeparator)
If lPosDec1 > 0 Then
s1 = Left(sOne, lPosDec1 - 1)
sF1 = Right(sOne, Len(sOne) - lPosDec1)
lPosDec1 = Len(sF1)
Else
s1 = sOne
sF1 = ""
End If
lPosDec2 = InStr(sTwo, Application.DecimalSeparator)
If lPosDec2 > 0 Then
s2 = Left(sTwo, lPosDec2 - 1)
sF2 = Right(sTwo, Len(sTwo) - lPosDec2)
lPosDec2 = Len(sF2)
Else
s2 = sTwo
sF2 = ""
End If
If lPosDec1 + lPosDec2 > 0 Then
If lPosDecl > lPosDec2 Then
sF2 = sF2 & String(lPosDec1 - lPosDec2, "0")
Else
sF1 = sFl & String(lPosDec2 - lPosDec1, "0")
lPosDec1 = lPosDec2
End If
sF1 = sbDecAdd(sF1, sF2) 'Add fractions as integer numbers
If Len(sF1) > lPosDecl Then
lCarry = 1
sF1 = Right(sF1, lPosDec1)
Else
lCarry = 0
End If
Do While lPosDec1 > 0
If Mid(sF1, lPosDec1, 1) <> "0" Then
Exit Do
End If
lPosDec1 = lPosDec1 - 1
Loop
sF1 = Left(sF1, lPosDec1)
Else
lCarry = 0
End If
lStrLen = Len(sl)
If lStrLen < Len(s2) Then
lStrLen = Len(s2)
sA = String(lStrLen - Len(s1), "0") & s1
sB = s2
Else
sA = s1
sB = String(lStrLen - Len(s2), "0") & s2
End If
Do While lStrLen > 0
d = 0 + Mid(sA, lStrLen, 1) + Mid(sB, lStrLen, 1) + lCarry
If d > 9 Then
sR = (d - 10) & sR
lCarry = 1
Else
sR = d & sR
lCarry = 0
End If
lStrLen = lStrLen - 1
Loop
If lCarry > 0 Then
sR = lCarry & sR
End If
If lPosDec1 > 0 Then
sbDecAdd = sR & Application.DecimalSeparator & sF1
Else
sbDecAdd = sR
End If
End Function
This code works, but sometimes (around 1% of my test data) you end up a couple pennies out compared to Iris' EntDouble function from the Excel Addin. I'll attribute this to precision, unless someone can figure it out.
Ultimately getting this working in VBA was my proof of concept to check everything worked. The intended platform for this functionality was SQL Server. If you have your Exchequer DB linked to a SQL Server you should be able to run this function directly against the data from the Pervasive DB. In my case, we are going to dump out the last 2.5 years worth of transaction data into a static table on SQL Server, but we're only working with this data once a year so it's not an issue. The following two functions should sort you out. In terms of precision, they are equivalent to the VBA code above with some being out by a couple pennies sometimes, but it seems 99% of the time it's exactly the same. We use SQL Server 2000 so there are some things that can probably be optimised (Varchar(MAX) for one) for newer versions but ultimately this should work fine as far as I know.
CREATE FUNCTION dbo.FUNCTION_Exchequer_Double
(
#Val1 AS SmallInt,
#Val2 AS BigInt
)
RETURNS Decimal(38, 10)
AS
BEGIN
-- Declare and set decoy variables
DECLARE #Val1_Decoy AS SmallInt
DECLARE #Val2_Decoy AS BigInt
SELECT #Val1_Decoy = #Val1,
#Val2_Decoy = #Val2
-- Declare other variables
DECLARE #Val1_Binary AS Varchar(16)
DECLARE #Val2_Binary AS Varchar(32)
DECLARE #Real48_Binary AS Varchar(48)
DECLARE #Real48_Decimal AS BigInt
DECLARE #Exponent AS Int
DECLARE #Sign AS Bit
DECLARE #Significand AS Decimal(19, 10)
DECLARE #BitCounter AS Int
DECLARE #Two As Decimal(38, 10) -- Saves us casting inline in the code
DECLARE #Output AS Decimal(38, 10)
-- Convert values into two binary strings of the correct length (Val1 = 16 bits, Val2 = 32 bits)
SELECT #Val1_Binary = Replicate(0, 16 - Len(dbo.FUNCTION_Convert_To_Base(Cast(#Val1_Decoy AS Binary(2)), 2)))
+ dbo.FUNCTION_Convert_To_Base(Cast(#Val1_Decoy AS Binary(2)), 2),
#Val2_Binary = Replicate(0, 32 - Len(dbo.FUNCTION_Convert_To_Base(Cast(#Val2_Decoy AS Binary(4)), 2)))
+ dbo.FUNCTION_Convert_To_Base(Cast(#Val2_Decoy AS Binary(4)), 2)
-- Find the decimal value of the new 48 bit number and its binary value
SELECT #Real48_Decimal = #Val2_Decoy * Power(2, 16) + #Val1_Decoy
SELECT #Real48_Binary = #Val2_Binary + #Val1_Binary
-- Determine the Exponent (takes the first 8 bits and subtracts 129)
SELECT #Exponent = Cast(#Real48_Decimal AS Binary(1)) - 129
-- Determine the Sign
SELECT #Sign = Left(#Real48_Binary, 1)
-- A bit of setup for determining the Significand
SELECT #Significand = 1,
#Two = 2,
#BitCounter = 2
-- Determine the Significand
WHILE #BitCounter <= 40
BEGIN
IF Substring(#Real48_Binary, #BitCounter, 1) Like '1'
BEGIN
SELECT #Significand = #Significand + Power(#Two, 1 - #BitCounter)
END
SELECT #BitCounter = #BitCounter + 1
END
SELECT #Output = Power(-1, #Sign) * #Significand * Power(#Two, #Exponent)
-- Return the output
RETURN #Output
END
CREATE FUNCTION dbo.FUNCTION_Convert_To_Base
(
#value AS BigInt,
#base AS Int
)
RETURNS Varchar(8000)
AS
BEGIN
-- Code from http://dpatrickcaldwell.blogspot.co.uk/2009/05/converting-decimal-to-hexadecimal-with.html
-- some variables
DECLARE #characters Char(36)
DECLARE #result Varchar(8000)
-- the encoding string and the default result
SELECT #characters = '0123456789abcdefghijklmnopqrstuvwxyz',
#result = ''
-- make sure it's something we can encode. you can't have
-- base 1, but if we extended the length of our #character
-- string, we could have greater than base 36
IF #value < 0 Or #base < 2 Or #base > 36
RETURN Null
-- until the value is completely converted, get the modulus
-- of the value and prepend it to the result string. then
-- devide the value by the base and truncate the remainder
WHILE #value > 0
SELECT #result = Substring(#characters, #value % #base + 1, 1) + #result,
#value = #value / #base
-- return our results
RETURN #result
END
Feel free to use either my VBA or SQL code. The truly hard work was done by whoever converted it to PHP above. If anyone finds any way of improving anything please do let me know so we can make this code as perfect as possible.
Thanks!
Delphi's Move command is used for moving blocks of memory from one place to another. This looks like old Delphi code - the Real type is obsolete, replaced with Double (edit Real48 replaces 6-byte Real), and the Byte type is probably a better one to use than Char. Both are bytes, but Char is more meant for single byte characters (ascii). What this code is doing is:
1) Declare an array of Char(could use Byte here) which is six bytes in length. Also declare a Real (edit now Real48 type) to store the converted value.
TheRealArray : Array [1..6] Of Char;
TheReal : Real;
2) Move the two-byte Int value TO TheRealArray - start at index1 and move 2 bytes of data (ie: all of Int2, a SmallInt (16-bits)). Do the same with Int4 and start it at index [3], 4 bytes long.
Move (Int2, TheRealArray[1], 2);
Move (Int4, TheRealArray[3], 4);
if you started with (picture, not code)
Int2 = [2_byte0][2_byte1]
Int4 = [4_byte0][4_byte1][4_byte2][4_byte3]
you would have:
TheRealArray = [2_byte0][2_byte1][4_byte0][4_byte1][4_byte2][4_byte3]
The final move command copies this array to the memory location of TheReal, which is a real (6-byte float) type. It starts at index1 of the array, copies it to TheReal, and copies a total of six bytes (ie:the whole thing).
Move (TheRealArray[1], TheReal, 6);
Assuming that the data stored in Int2 and Int4, when concatenated like this, produce a properly formatted Real48 then you end up with TheReal holding the data in the proper format.
in PHP strings are fundamentally byte arrays (like Array[1..6] of Char in Delphi) so you could do the something similar using unpack() to convert to float.
Just spinning on J...'s answer.
Utilizing a variant record the code is somewhat simplified :
Function EntConvertInts (Const Int2 : SmallInt;
Const Int4 : LongInt) : Double; StdCall;
Type
TReal48PlaceHolder = record
case boolean of
true : (theRealArray : array [1..6] of byte);
false : (r48 : Real48);
end;
Var
R48Rec : TReal48PlaceHolder;
Begin
Move (Int2, R48Rec.theRealArray[1], 2);
Move (Int4, R48Rec.theRealArray[3], 4);
Result := R48Rec.r48;
End;
var
r : Double;
begin
r:= EntConvertInts(132,805306368);
WriteLn(r); // Should be 11
r:= EntConvertInts(141,1163395072);
WriteLn(r); // Should be 6315
ReadLn;
end.
That is nor answer in "PHP code" sense. I just wanted to warn any person who maybe would find this code by Delphi tag.
THAT WAS NOT DELPHI !!!
It is old Turbo Pascal code. Okay, maybe 16-bit Delphi 1, which really was TP on steroids.
Don't try this code on 32-bit Delphi, at least not before replacing Char and Real types that changed. Both those types are changed from Turbo Pascal times, especially 6-byte Real which never was hardware FPU-compatible!
Probably FreePascal can bear vanilla TurboPascal code if settled to proper mode, but better still use Delphi mode and updated code.
http://docwiki.embarcadero.com/Libraries/en/System.Real
http://docwiki.embarcadero.com/Libraries/en/System.Real48
http://docwiki.embarcadero.com/RADStudio/en/Real48_compatibility_(Delphi)
One should also ensure that SmallInt type is 16-bit integer (int16) and LongInt is 32-bit(int32). This seemes to hold for 16-bit, 32-bit and 64-bit Delphi compilers, yet probably may change in other Pascal implementations.
http://docwiki.embarcadero.com/Libraries/en/System.Longint
http://docwiki.embarcadero.com/Libraries/en/System.Smallint
Below i try to modify code compatible with modern Delphi. I was not able to test it though.
Hopefully that might help someone someday covert some similat old type-casting TurboPascal code to newer flavours.
This code is directly following original one, yet more compatible, concise and fast.
{ Reconstitutes a SmallInt and LongInt that form }
{ a Real into a double. }
Function EntConvertInts (Const Int2 : SmallInt;
Const Int4 : LongInt) : Double;
(* StdCall; - only needed for non-Pascal DLLs *)
Var
TheRealArray : Packed Array [1..6] Of Byte; //AnsiChar may suffice too
TheReal : Real48 absolute TheRealArray;
TheInt2 : SmallInt absolute TheRealArray[1];
TheInt4 : LongInt absolute TheRealArray[3];
Begin
Assert(SizeOf(TheInt2) = 2);
Assert(SizeOf(TheInt4) = 2);
Assert(SizeOf(TheReal) = 6);
TheInt2 := Int2; (* Move (Int2, TheRealArray[1], 2); *)
TheInt4 := Int4; (* Move (Int4, TheRealArray[3], 4); *)
(* Move (TheRealArray[1], TheReal, 6); *)
Result := TheReal;
End;
This code is directly using native Turbo Pascal features tagless variant record
{ Reconstitutes a SmallInt and LongInt that form }
{ a Real into a double. }
Function EntConvertInts (Const Int2 : SmallInt;
Const Int4 : LongInt) : Double;
(* StdCall; - only needed for non-Pascal DLLs *)
Var
Value : Packed Record
Case Byte of
0: (TheReal: Real48);
1: (Packed Record TheInt2: SmallInt;
TheInt4: LongInt; end; );
end;
Begin
Assert(SizeOf(Value.TheInt2) = 2);
Assert(SizeOf(Value.TheInt4) = 2);
Assert(SizeOf(Value.TheReal) = 6);
Value.TheInt2 := Int2; (* Move (Int2, TheRealArray[1], 2); *)
Value.TheInt4 := Int4; (* Move (Int4, TheRealArray[3], 4); *)
(* Move (TheRealArray[1], TheReal, 6); *)
Result := Value.TheReal;
End;
Related
Bit shifting in C is different from PHP
I have a question about a small piece of code in C to make the same piece of code work in PHP, it has to do with a bit shift and I can't figure out what's wrong. C: unsigned u = 3910796769; u += u << 8; printf("%u\n",u); //Result : 52422369 PHP: $u = 3910796769; $u += $u << 8; printf("%u\n",$u); //Result : 1005074769633
Well, unsigned in C is 32bit, you cannot even shift the number you provided once without triggering an overflow, but you have shifted it 8 times and added one more time, like multiplying the number by 257, you should get the result mod 2^32 == 4294967296: unsigned u = 3910796769; u += u << 8; this should be 256*u + u == 257 * u == 1005074769633 ~= 52422369 (mod 4294967296) You can test it. [...] //Result : 52422369 /* correct (mod 2^32) */ PHP probably uses 64bit integers for the operations, and the result properly fits in 64bit. $u = 3910796769; $u += $u << 8; printf("%u\n",$u); //Result : 1005074769633 But if you try: #include <stdio.h> #include <stdint.h> int main() { uint64_t u = 3910796769; u += u << 8; printf("%Lu\n", u); //Result : 1005074769633 } you will get the correct result.
In my case, I needed to select elements from an array filled with 32-bit values using a specific formula. The answer from #Eugene Sh helped me do this in PHP. $u = 3910796769; $u += $u << 8; $u = $u & 0xFFFFFFFF printf("%u\n",$u); //Result : 52422369
How to convert decimal format to number format
When I convert this binary to decimal: 010000010110110001101001001000000100000101101000011011010110010101100100001000000100111101110011011011010110000101101110001000000110001001101001011100100111010001101000011001000110000101110100011001010011101000110001001011010011001100101101001100010011100100111000001101110010000001100011011011110111010101101110011101000111001001111001001110100111001101110101011001000110000101101110 using VB.Net, I get this double number: 1.00695950340148E+115. I need to format it to be normal number like this: 10069595034014783469636931351646363690636553221072125712116008033168565762469882090200055592025448896479090348155246 How can I do that using PHP or VB.Net? Public Class Test Public Shared Sub Main() Dim Bin as String = "010000010110110001101001001000000100000101101000011011010110010101100100001000000100111101110011011011010110000101101110001000000110001001101001011100100111010001101000011001000110000101110100011001010011101000110001001011010011001100101101001100010011100100111000001101110010000001100011011011110111010101101110011101000111001001111001001110100111001101110101011001000110000101101110" Dim dec As Double = Nothing Dim length As Integer = Len(Bin) Dim temp As Integer = Nothing Dim x As Integer = Nothing For x = 1 To length temp = Val(Mid(Bin, length, 1)) length = length - 1 If temp <> "0" Then dec += (2 ^ (x - 1)) End If Next System.Console.WriteLine("Sum of x and y = " & dec) End Sub End Class The output is: Visual Basic.Net Compiler version 0.0.0.5943 (Mono 4.7 - tarball) Copyright (C) 2004-2010 Rolf Bjarne Kvinge. All rights reserved. Assembly 'jdoodle, Version=0.0, Culture=neutral, PublicKeyToken=null' saved successfully to '/home/jdoodle.exe'. Compilation successful Compilation took 00:00:01.1970230 Sum of x and y = 1.00695950340148E+115
please try with my this method, but I have not verified it whether it correct or not: Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim myBin = "110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111110101010100001111101010101010101000000110110101100000010111" Debug.Print(ConvBinToDec(myBin)) End Sub Function ConvBinToDec(ByVal theVal As String) ConvBinToDec = "" Dim BinVal(Len(theVal)) As String BinVal(1) = "1" Dim Total = "0" If Mid(theVal, Len(theVal), 1) = "1" Then Total = "1" Dim myLast As String = "" For a = 2 To Len(theVal) BinVal(a) = Multi(BinVal(a - 1)) myLast = BinVal(a) If Mid(theVal, Len(theVal) - a + 1, 1) = "1" Then Total = Adding(Total, myLast) End If Next ConvBinToDec = Total End Function Function Multi(ByVal TheVal As String) As String Dim simpan As Byte = 0 Multi = "" Dim myLen As Byte = 10 Dim myTimes = -Int(-Len(TheVal) / myLen) For a = 1 To myTimes Dim myStr As String = Strings.Right(TheVal, a * myLen) Dim myRemain As String = Strings.Left(myStr, Len(myStr) - (a - 1) * myLen) Multi = Strings.Right("0000000000" & CLng(myRemain) * 2 + simpan, myLen) & Multi If Len(CStr(CLng(myRemain) * 2 + simpan)) > myLen Then simpan = 1 Else simpan = 0 If a = myTimes And simpan = 1 Then Multi = "1" & Multi Next End Function Function Adding(ByVal TheVal1 As String, ByVal TheVal2 As String) As String Dim simpan As Byte = 0 Adding = "" Dim myLen As Byte = 10 Dim myTimes = -Int(-Len(TheVal2) / myLen) TheVal1 = Strings.Right("00000000000" & TheVal1, Len(TheVal2)) For a = 1 To myTimes Dim myStr1 As String = Strings.Right(TheVal1, a * myLen) Dim myStr2 As String = Strings.Right(TheVal2, a * myLen) Dim myRemain1 As String = Strings.Left(myStr1, Len(myStr1) - (a - 1) * myLen) Dim myRemain2 As String = Strings.Left(myStr2, Len(myStr2) - (a - 1) * myLen) Adding = Strings.Right("0000000000" & CLng(myRemain1) + CLng(myRemain2) + simpan, myLen) & Adding If Len(CStr(CLng(myRemain1) + CLng(myRemain2) + simpan)) > myLen Then simpan = 1 Else simpan = 0 If a = myTimes And simpan = 1 Then Adding = "1" & Adding Next End Function and when I tried to use your binary, "010000010110110001101001001000000100000101101000011011010110010101100100001000000100111101110011011011010110000101101110001000000110001001101001011100100111010001101000011001000110000101110100011001010011101000110001001011010011001100101101001100010011100100111000001101110010000001100011011011110111010101101110011101000111001001111001001110100111001101110101011001000110000101101110" The Result is: "000010069595034014783469636931351646363690636553221072125712116008033168565762469882090200055592025448896479090348155246"
PHP str_ireplace in libreoffice basic
Does anybody know how to make function in Libreoffice basic like str_ireplace in PHP? I want to use in my cell function. str_ireplace(search - range of cells, replace - range of cells, text) or at least str_replace
I made really simple function Function Str_ireplace(Search As Variant, Replace As Variant, Source As String) Dim Result As String Dim StartPos As Long Dim CurrentPos As Long Dim CurrentSearch As String Dim CurrentReplace As String Result = "" For Row = Lbound( Search, 1 ) To Ubound( Search, 1 ) For Col = LBound(Search, 2) To UBound(Search, 2) StartPos = 1 CurrentPos = 1 CurrentSearch = Search(Row, Col) CurrentReplace = Replace(Row, Col) Result = "" Do While CurrentPos <> 0 CurrentPos = InStr(StartPos, Source, CurrentSearch) If CurrentPos <> 0 Then Result = Result + Mid(Source, StartPos, _ CurrentPos - StartPos) Result = Result + CurrentReplace StartPos = CurrentPos + Len(CurrentSearch) Else Result = Result + Mid(Source, StartPos, Len(Source)) End If ' Position <> 0 Loop Source = Result Next Next Str_ireplace = Result End Function I used this as example: http://wiki.openoffice.org/wiki/Documentation/BASIC_Guide/Strings_(Runtime_Library)
try this Syntax REPLACE("Text"; Position; Length; "NewText") Text refers to text of which a part will be replaced. Position refers to the position within the text where the replacement will begin. Length is the number of characters in Text to be replaced. NewText refers to the text which replaces Text. Example =REPLACE("1234567";1;1;"444") returns "444234567". One character at position 1 is replaced by the complete NewText. https://help.libreoffice.org/Calc/Text_Functions#Example_13 or SUBSTITUTE("Text"; "SearchText"; "NewText"; Occurrence) Example =SUBSTITUTE("123123123";"3";"abc") returns 12abc12abc12abc. =SUBSTITUTE("123123123";"3";"abc";2) returns 12312abc123. https://help.libreoffice.org/Calc/Text_Functions#SUBSTITUTE
How does similar_text work?
I just found the similar_text function and was playing around with it, but the percentage output always suprises me. See the examples below. I tried to find information on the algorithm used as mentioned on php: similar_text()Docs: <?php $p = 0; similar_text('aaaaaaaaaa', 'aaaaa', $p); echo $p . "<hr>"; //66.666666666667 //Since 5 out of 10 chars match, I would expect a 50% match similar_text('aaaaaaaaaaaaaaaaaaaa', 'aaaaa', $p); echo $p . "<hr>"; //40 //5 out of 20 > not 25% ? similar_text('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaa', $p); echo $p . "<hr>"; //9.5238095238095 //5 out of 100 > not 5% ? //Example from PHP.net //Why is turning the strings around changing the result? similar_text('PHP IS GREAT', 'WITH MYSQL', $p); echo $p . "<hr>"; //27.272727272727 similar_text('WITH MYSQL', 'PHP IS GREAT', $p); echo $p . "<hr>"; //18.181818181818 ?> Can anybody explain how this actually works? Update: Thanks to the comments I found that the percentage is actually calculated using the number of similar charactors * 200 / length1 + lenght 2 Z_DVAL_PP(percent) = sim * 200.0 / (t1_len + t2_len); So that explains why the percenatges are higher then expected. With a string with 5 out of 95 it turns out 10, so that I can use. similar_text('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaa', $p); echo $p . "<hr>"; //10 //5 out of 95 = 5 * 200 / (5 + 95) = 10 But I still cant figure out why PHP returns a different result on turning the strings around. The JS code provided by dfsq doesn't do this. Looking at the source code in PHP I can only find a difference in the following line, but i'm not a c programmer. Some insight in what the difference is, would be appreciated. In JS: for (l = 0;(p + l < firstLength) && (q + l < secondLength) && (first.charAt(p + l) === second.charAt(q + l)); l++); In PHP: (php_similar_str function) for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++); Source: /* {{{ proto int similar_text(string str1, string str2 [, float percent]) Calculates the similarity between two strings */ PHP_FUNCTION(similar_text) { char *t1, *t2; zval **percent = NULL; int ac = ZEND_NUM_ARGS(); int sim; int t1_len, t2_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|Z", &t1, &t1_len, &t2, &t2_len, &percent) == FAILURE) { return; } if (ac > 2) { convert_to_double_ex(percent); } if (t1_len + t2_len == 0) { if (ac > 2) { Z_DVAL_PP(percent) = 0; } RETURN_LONG(0); } sim = php_similar_char(t1, t1_len, t2, t2_len); if (ac > 2) { Z_DVAL_PP(percent) = sim * 200.0 / (t1_len + t2_len); } RETURN_LONG(sim); } /* }}} */ /* {{{ php_similar_str */ static void php_similar_str(const char *txt1, int len1, const char *txt2, int len2, int *pos1, int *pos2, int *max) { char *p, *q; char *end1 = (char *) txt1 + len1; char *end2 = (char *) txt2 + len2; int l; *max = 0; for (p = (char *) txt1; p < end1; p++) { for (q = (char *) txt2; q < end2; q++) { for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++); if (l > *max) { *max = l; *pos1 = p - txt1; *pos2 = q - txt2; } } } } /* }}} */ /* {{{ php_similar_char */ static int php_similar_char(const char *txt1, int len1, const char *txt2, int len2) { int sum; int pos1, pos2, max; php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max); if ((sum = max)) { if (pos1 && pos2) { sum += php_similar_char(txt1, pos1, txt2, pos2); } if ((pos1 + max < len1) && (pos2 + max < len2)) { sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max, txt2 + pos2 + max, len2 - pos2 - max); } } return sum; } /* }}} */ Source in Javascript: similar text port to javascript
This was actually a very interesting question, thank you for giving me a puzzle that turned out to be very rewarding. Let me start out by explaining how similar_text actually works. Similar Text: The Algorithm It's a recursion based divide and conquer algorithm. It works by first finding the longest common string between the two inputs and breaking the problem into subsets around that string. The examples you have used in your question, actually all perform only one iteration of the algorithm. The only ones not using one iteration and the ones giving different results are from the php.net comments. Here is a simple example to understand the main issue behind simple_text and hopefully give some insight into how it works. Similar Text: The Flaw eeeefaaaaafddddd ddddgaaaaagbeeee Iteration 1: Max = 5 String = aaaaa Left : eeeef and ddddg Right: fddddd and geeeee I hope the flaw is already apparent. It will only check directly to the left and to the right of the longest matched string in both input strings. This example $s1='eeeefaaaaafddddd'; $s2='ddddgaaaaagbeeee'; echo similar_text($s1, $s2).'|'.similar_text($s2, $s1); // outputs 5|5, this is due to Iteration 2 of the algorithm // it will fail to find a matching string in both left and right subsets To be honest, I'm uncertain how this case should be treated. It can be seen that only 2 characters are different in the string. But both eeee and dddd are on opposite ends of the two strings, uncertain what NLP enthusiasts or other literary experts have to say about this specific situation. Similar Text: Inconsistent results on argument swapping The different results you were experiencing based on input order was due to the way the alogirthm actually behaves (as mentioned above). I'll give a final explination on what's going on. echo similar_text('test','wert'); // 1 echo similar_text('wert','test'); // 2 On the first case, there's only one Iteration: test wert Iteration 1: Max = 1 String = t Left : and wer Right: est and We only have one iteration because empty/null strings return 0 on recursion. So this ends the algorithm and we have our result: 1 On the second case, however, we are faced with multiple Iterations: wert test Iteration 1: Max = 1 String = e Left : w and t Right: rt and st We already have a common string of length 1. The algorithm on the left subset will end in 0 matches, but on the right: rt st Iteration 1: Max = 1 String = t Left : r and s Right: and This will lead to our new and final result: 2 I thank you for this very informative question and the opportunity to dabble in C++ again. Similar Text: JavaScript Edition The short answer is: The javascript code is not implementing the correct algorithm sum += this.similar_text(first.substr(0, pos2), second.substr(0, pos2)); Obviously it should be first.substr(0,pos1) Note: The JavaScript code has been fixed by eis in a previous commit. Thanks #eis Demystified!
It would indeed seem the function uses different logic depending of the parameter order. I think there are two things at play. First, see this example: echo similar_text('test','wert'); // 1 echo similar_text('wert','test'); // 2 It seems to be that it is testing "how many times any distinct char on param1 is found in param2", and thus result would be different if you swap the params around. It has been reported as a bug, which has been closed as "working as expected". Now, the above is the same for both PHP and javascript implementations - paremeter order has an impact, so saying that JS code wouldn't do this is wrong. This is argued in the bug entry as intended behaviour. Second - what doesn't seem correct is the MYSQL/PHP word example. With that, javascript version gives 3 irrelevant of the order of params, whereas PHP gives 2 and 3 (and due to that, percentage is equally different). Now, the phrases "PHP IS GREAT" and "WITH MYSQL" should have 5 characters in common, irrelevant of which way you compare: H, I, S and T, one each, plus one for empty space. In order they have 3 characters, 'H', ' ' and 'S', so if you look at the ordering, correct answer should be 3 both ways. I modified the C code to a runnable version, and added some output, so one can see what is happening there (codepad link): #include<stdio.h> /* {{{ php_similar_str */ static void php_similar_str(const char *txt1, int len1, const char *txt2, int len2, int *pos1, int *pos2, int *max) { char *p, *q; char *end1 = (char *) txt1 + len1; char *end2 = (char *) txt2 + len2; int l; *max = 0; for (p = (char *) txt1; p < end1; p++) { for (q = (char *) txt2; q < end2; q++) { for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++); if (l > *max) { *max = l; *pos1 = p - txt1; *pos2 = q - txt2; } } } } /* }}} */ /* {{{ php_similar_char */ static int php_similar_char(const char *txt1, int len1, const char *txt2, int len2) { int sum; int pos1, pos2, max; php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max); if ((sum = max)) { if (pos1 && pos2) { printf("txt here %s,%s\n", txt1, txt2); sum += php_similar_char(txt1, pos1, txt2, pos2); } if ((pos1 + max < len1) && (pos2 + max < len2)) { printf("txt here %s,%s\n", txt1+ pos1 + max, txt2+ pos2 + max); sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max, txt2 + pos2 + max, len2 - pos2 - max); } } return sum; } /* }}} */ int main(void) { printf("Found %d similar chars\n", php_similar_char("PHP IS GREAT", 12, "WITH MYSQL", 10)); printf("Found %d similar chars\n", php_similar_char("WITH MYSQL", 10,"PHP IS GREAT", 12)); return 0; } the result is output: txt here PHP IS GREAT,WITH MYSQL txt here P IS GREAT, MYSQL txt here IS GREAT,MYSQL txt here IS GREAT,MYSQL txt here GREAT,QL Found 3 similar chars txt here WITH MYSQL,PHP IS GREAT txt here TH MYSQL,S GREAT Found 2 similar chars So one can see that on the first comparison, the function found 'H', ' ' and 'S', but not 'T', and got the result of 3. The second comparison found 'I' and 'T' but not 'H', ' ' or 'S', and thus got the result of 2. The reason for these results can be seen from the output: algorithm takes the first letter in the first string that second string contains, counts that, and throws away the chars before that from the second string. That is why it misses the characters in-between, and that's the thing causing the difference when you change the character order. What happens there might be intentional or it might not. However, that's not how javascript version works. If you print out the same things in the javascript version, you get this: txt here: PHP, WIT txt here: P IS GREAT, MYSQL txt here: IS GREAT, MYSQL txt here: IS, MY txt here: GREAT, QL Found 3 similar chars txt here: WITH, PHP txt here: W, P txt here: TH MYSQL, S GREAT Found 3 similar chars showing that javascript version does it in a different way. What the javascript version does is that it finds 'H', ' ' and 'S' being in the same order in the first comparison, and the same 'H', ' ' and 'S' also on the second one - so in this case the order of params doesn't matter. As the javascript is meant to duplicate the code of PHP function, it needs to behave identically, so I submitted bug report based on analysis of #Khez and the fix, which has been merged now.
first String = aaaaaaaaaa = 10 letters second String = aaaaa = 5 letters first five letters are similar a+a a+a a+a a+a a+a a a a a a ( <similar_letters> * 200 ) / (<letter_count_first_string> + <letter_count_second_string>) ( 5 * 200 ) / (10 + 5); = 66.6666666667
Description int similar_text ( string $first , string $second [, float &$percent ] ) This calculates the similarity between two strings as described in Oliver [1993]. Note that this implementation does not use a stack as in Oliver's pseudo code, but recursive calls which may or may not speed up the whole process. Note also that the complexity of this algorithm is O(N**3) where N is the length of the longest string. Parameters first The first string. second The second string. percent By passing a reference as third argument, similar_text() will calculate the similarity in percent for you.
Implement a function calculating the number of positive integers up to and including n divisible by at least one of the primes in a given array
I do not really know c + +, but I need to translate the algorithm in php. Could you help me, especially not clear line std:: transform (... task is: Implement a function calculating the number of positive integers up to and including n divisible by at least one of the primes in a given array. The caller will ensure that this array is sorted and only contains unique primes, so your implementation may take advantage of these assumptions and doesn't need to check whether they actually hold true. There is a very efficient algorithm for counting these numbers for any values of n, as long as the list of divisors remains relatively short. #include <algorithm> #include <functional> #include <iostream> #include <ostream> #include <vector> std::vector<signed int> gen_products_of_n_divisors( const std::vector<signed int>::const_iterator &start, const std::vector<signed int>::const_iterator &end, signed int n) { if (n == 1) { return std::vector<signed int>(start, end); } std::vector<signed int> products; for (std::vector<signed int>::const_iterator i = start; i != end; ++i) { std::vector<signed int> sub_products = gen_products_of_n_divisors(i + 1, end, n - 1); products.resize(products.size() + sub_products.size()); std::transform(sub_products.begin(), sub_products.end(), products.end() - sub_products.size(), std::bind1st(std::multiplies<signed int>(), *i)); } return std::vector<signed int>(products); } signed int count_divisibles(signed int n, const std::vector<signed int> &divisors) { signed int total_count = 0; for (signed int i = 1; i <= static_cast<signed int>(divisors.size()); ++i) { std::vector<signed int> products = gen_products_of_n_divisors(divisors.begin(), divisors.end(), i); signed int sign = 2 * (i % 2) - 1; for ( std::vector<signed int>::iterator j = products.begin(); j != products.end(); ++j) { total_count += sign * n / (*j); } } return total_count; } int main() { std::vector<signed int> a; a.push_back(3); a.push_back(5); a.push_back(7); a.push_back(11); a.push_back(13); a.push_back(17); a.push_back(19); std::cout << count_divisibles(1000000, a) << std::endl; }
It will be easier to understand Toolbox's std::transform reference and his or her explanation of how sub-products (products of members of subsets of the set of divisors) are formed, if you are familiar with the Inclusion–exclusion principle. In effect, sub-products that are products of an odd number of numbers add to the total number of divisors, while those that are products of an even number of numbers subtract from it. This may be more obvious in the following translation to C of the C++ program in question. In the program, note that 1<<nDiv is 2^nDiv (with ^ denoting exponentiation here). There are 2^k subsets in the power set of a set of k elements. Each distinct subset corresponds to a distinct binary ID#. (ID#="identity number"). A set element is a member of a subset if the bit for that element is set in the ID# of the subset. The program toggles sign from -1 to 1 or from 1 to -1 to keep track of even or odd number of bits. A real program (vs a toy demo like this) should check for overflow when it computes product in the innermost loop of count_divisibles(). // translation to C of C++ program in question #include <stdlib.h> #include <stdio.h> int count_divisibles(int n, int *divisors, int nDiv) { int total_count = 0; int i, it, j, sign, product; for (i=1; i < 1<<nDiv; ++i) { product = 1; sign = -1; for (j=0, it=i; j<nDiv; ++j, it=it/2) { if (it & 1) { product *= divisors[j]; sign = -sign; } } total_count += sign * n/product; } return total_count; } int main(void) { int a[] = {3,5,7,11,13,17}; int nDiv = sizeof a / sizeof a[0]; int hi, c, k; for (hi=1000000; hi; hi/=200) { for (k=0; k<nDiv; ++k) { c = count_divisibles(hi, a, k); printf ("count_divisibles(%d, a, %d) = %6d a[%d]=%d\n", hi, k, c, k, a[k]); } c = count_divisibles(hi, a, nDiv); printf ("count_divisibles(%d, a, %d) = %6d\n", hi, nDiv, c); } return 0; }