Bcmath adding decimal places randomly

Tag: php , bcmath Author: zhouxuesong Date: 2014-04-23

I have a PHP function I got from the web that uses bcmath functions:

function SteamID64to32($steamId64) {
    $iServer = "1";
    if(bcmod($steamId64, "2") == "0") {
        $iServer = "0";
    }
    $steamId64 = bcsub($steamId64,$iServer);
    if(bccomp("76561197960265728",$steamId64) == -1) {
        $steamId64 = bcsub($steamId64,"76561197960265728");
    }
    $steamId64 = bcdiv($steamId64, "2");
    return ("STEAM_0:" . $iServer . ":" . $steamId64);
}

For any valid input the function will at random times add ".0000000000" to the output.

Ex:

$input = 76561198014791430;
SteamID64to32($input);

//result for each run
STEAM_0:0:27262851
STEAM_0:0:27262851.0000000000
STEAM_0:0:27262851
STEAM_0:0:27262851
STEAM_0:0:27262851.0000000000
STEAM_0:0:27262851
STEAM_0:0:27262851.0000000000
STEAM_0:0:27262851.0000000000
STEAM_0:0:27262851.0000000000

This was tested on 2 different nginx servers running php-fpm. Please help me understand what is wrong here. Thanks

Best Answer

The answer provided by JD doesn't account for all possible STEAM_ ID types, namely, anything that is in the STEAM_0:1 range. This block of code will:

<?php

function Steam64ToSteam32($Steam64ID)
{
    $offset = $Steam64ID - 76561197960265728;
    $id = ($offset / 2);
    if($offset % 2 != 0)
    {
        $Steam32ID = 'STEAM_0:1:' . bcsub($id, '0.5');
    }
    else
    {
        $Steam32ID = "STEAM_0:0:" . $id;
    }
    return $Steam32ID;
}

echo Steam64ToSteam32(76561197960435530);
echo "<br/>";
echo Steam64ToSteam32(76561197990323733);
echo "<br/>";
echo Steam64ToSteam32(76561198014791430);
?>

This outputs

STEAM_0:0:84901
STEAM_0:1:15029002
STEAM_0:0:27262851

The first one is for a Valve employee and someone who's had a Steam account since 2003 (hence the low ID), the second is a random profile I found on VACBanned which had an ID in the STEAM_0:1 range. The third is the ID you provided in your sample code.

Other Answer1

I found this: SteamProfile

/**
 *      This file is part of SteamProfile.
 *
 *      Written by Nico Bergemann <[email protected]>
 *      Copyright 2009 Nico Bergemann
 *
 *      SteamProfile is free software: you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation, either version 3 of the License, or
 *      (at your option) any later version.
 *
 *      SteamProfile is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with SteamProfile.  If not, see <http://www.gnu.org/licenses/>.
 */

// thanks to voogru for the id transformation algorithm (http://forums.alliedmods.net/showthread.php?t=60899)

class SteamID {
        private $sSteamID = '';
        private $sSteamComID = '';

        const STEAMID64_BASE = '76561197960265728';

        public function __construct($sID) {
                // make sure the bcmath extension is loaded
                if(!extension_loaded('bcmath')) {
                        throw new RuntimeException("BCMath extension required");
                }

                if($this->isValidSteamID($sID)) {
                        $this->sSteamID = $sID;
                        $this->sSteamComID = $this->convertToSteamComID($sID);
                } elseif($this->isValidComID($sID)) {
                        $this->sSteamID = $this->convertToSteamID($sID);
                        $this->sSteamComID = $sID;
                } else {
                        $this->sSteamID = '';
                        $this->sSteamComID = '';
                }
        }

        public function getSteamID() {
                return $this->sSteamID;
        }

        public function getSteamComID() {
                return $this->sSteamComID;
        }

        public function isValid() {
                return $this->sSteamID != '';
        }

        private function isValidSteamID($sSteamID) {
                return preg_match('/^(STEAM_)?[0-5]:[0-9]:\d+$/i', $sSteamID);
        }

        private function isValidComID($sSteamComID) {
                // anything else than a number is invalid
                // (is_numeric() doesn't work for 64 bit integers)
                if(!preg_match('/^\d+$/i', $sSteamComID)) {
                        return false;
                }

                // the community id must be bigger than STEAMID64_BASE
                if(bccomp(self::STEAMID64_BASE, $sSteamComID) == 1) {
                        return false;
                }

                // TODO: Upper limit?

                return true;
        }

        private function convertToSteamComID($sSteamID) {
                $aTMP = explode(':', $sSteamID);

                $sServer = $aTMP[1];
                $sAuth = $aTMP[2];

                if((count($aTMP) == 3) && $sAuth != '0' && is_numeric($sServer) && is_numeric($sAuth)) {
                        $sComID = bcmul($sAuth, "2"); // multipy Auth-ID with 2
                        $sComID = bcadd($sComID, $sServer); // add Server-ID
                        $sComID = bcadd($sComID, self::STEAMID64_BASE); // add this odd long number

                        // It seems that PHP appends ".0000000000" at the end sometimes.
                        // I can't find a reason for this, so I'll take the dirty way...
                        $sComID = str_replace('.0000000000', '', $sComID);

                        return $sComID;
                } else {
                        throw new RuntimeException("Unable to convert Steam-ID");
                }
        }

        private function convertToSteamID($sSteamComID) {
                $sServer = bcmod($sSteamComID, '2') == '0' ? '0' : '1';
                $sCommID = bcsub($sSteamComID, $sServer);
                $sCommID = bcsub($sCommID, self::STEAMID64_BASE);
                $sAuth = bcdiv($sCommID, '2');

                return "STEAM_0:$sServer:$sAuth";
        }
}

comments:

What about SteamIDs of type STEAM_0:1 ?