I renamed BonusTechCategories into TechCategoryCostModifiers and made it work as the new name indicates, which is to modify the cost of a tech. If you set it to -10 and the tech cost 100 ideas to research, then CvPlayer::getCostToResearch() will return 90 instead of 100 (if tech is of the chosen tech category).
I also added a modifier for handicap to that function meaning the AI will now get a multiplier for difficulty level. It was scaled for game speed, but not difficulty. I borrowed the setting for father points, but maybe we should add a new int to handicap XML for this purpose.
There is a problem though. I decided to store modifiers as doubles as it is easy to stack multiple bonuses correctly (vanilla fails to do this) and all it has to do is to multiply it to the number it has to modify.
There is a catch though, which is lack of precision. If the multiplier starts at 1 (100%) and we multiply it by 1.1 (+10%) and remove the 10% bonus again, it might not go back to 1. I added code to take this into account and make it round up in case it ends up at 0.9999. However adding and removing 50 times and we have a problem. Also the rounding issue might be CPU specific, meaning we could end up with a game, which doesn't behave identically on all computers, which is a network killer (I still want to fix network games).
I looked into using ints and see if there is something online about using ints for decimal numbers. There is and it's called "binary coded decimal". The financial sector is using it because it has great accuracy (they don't want to have rounding errors for some reason

). However it doesn't look performance friendly and just switching to a system like that is not enough to make our calculations entirely consistent (well it is, except for the case where a player joins a network game during the game, which I would like to work too).
Solution:
I want to make a new class. This class has a list of ints. You can add to that list with ::add(int). At some point you would like to get the resulting int, which can then be obtained by ::get(). It can then be used to do something like this: (not the greatest example, but it should tell the idea).
Internally get() should sort the list and then multiply the numbers together in a way, which will not cause overflows, but at the same time has as little rounding issues as possible. More importantly it should always return the same given the same numbers, regardless of order (hence the sort).
Next up is making a class, which makes the work of the current JIT arrays in the player cache. However it should have a vector of this new class (be it 1D or 2D). That way it can recalculate with great accuracy every time a CivEffect is added or removed and it will
ALWAYS give the same result for a specific set of CivEffects, regardless of history (no inaccuracy inherited from lost CivEffects or anything like that). This cache will need a JIT array for caching the output though or it will slow down the game. Yeah, a cache for the cache
This will use more memory than the current setup, but we should be able to survive that and it will be worth it to regain consistent calculations.
Now I just need a proper name for this new class.